aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-rete
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2023-09-14 19:29:36 +0200
committerLibravatar GitHub <noreply@github.com>2023-09-14 19:29:36 +0200
commit98ed3b6db5f4e51961a161050cc31c66015116e8 (patch)
tree8bfd6d9bc8d6ed23b9eb0f889dd40b6c24fe8f92 /subprojects/viatra-runtime-rete
parentMerge pull request #38 from nagilooh/design-space-exploration (diff)
parentMerge remote-tracking branch 'upstream/main' into partial-interpretation (diff)
downloadrefinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.gz
refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.zst
refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.zip
Merge pull request #39 from kris7t/partial-interpretation
Implement partial interpretation based model generation
Diffstat (limited to 'subprojects/viatra-runtime-rete')
-rw-r--r--subprojects/viatra-runtime-rete/about.html26
-rw-r--r--subprojects/viatra-runtime-rete/build.gradle.kts15
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java474
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java369
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java38
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java120
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java114
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java26
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java278
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java212
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java279
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java247
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java106
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java117
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java212
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java26
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java209
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java68
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java208
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java551
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java50
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java171
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java90
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java390
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java86
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java949
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java162
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java49
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java205
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java30
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java65
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java180
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java25
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java75
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java311
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java183
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java28
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java348
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java199
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java76
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java76
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java71
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java41
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java284
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java34
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java193
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java55
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java54
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java88
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java47
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java21
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java176
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java127
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java121
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java51
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java49
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingAlg.java226
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingTcRelation.java259
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/CountingListener.java36
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/IncSCCAlg.java609
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/DFSPathFinder.java146
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Edge.java38
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/GraphHelper.java169
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/IGraphPathFinder.java67
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/ITcRelation.java31
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Tuple.java60
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java148
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/PKAlg.java179
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java143
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCProperty.java37
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCResult.java81
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java73
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java174
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeObserver.java12
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java69
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java85
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/util/CollectionHelper.java64
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/DotGenerator.java160
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/Graph.java185
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalGraphDataSource.java37
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalWrapper.java110
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphDataSource.java70
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphObserver.java55
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcDataSource.java82
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcObserver.java39
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java49
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java46
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java30
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java100
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java35
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java579
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java463
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java61
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java64
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java43
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java50
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java43
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java111
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java109
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java108
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java171
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java31
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java408
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java30
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java62
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java376
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java346
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java39
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java28
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java85
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java34
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java14
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java729
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java123
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java82
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java19
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java31
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java103
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java467
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java19
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java32
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java34
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java124
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java164
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java86
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java149
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java36
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java171
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java216
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java81
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java102
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java48
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java81
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java27
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java30
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java74
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java95
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java33
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java32
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java30
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java78
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java23
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java109
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java117
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java163
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java218
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java135
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java150
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java125
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java63
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java54
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java138
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java37
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java79
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java85
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java154
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java41
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java69
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java52
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java125
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java126
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java63
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java161
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java49
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java147
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java48
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java61
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java321
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java44
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java24
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java54
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java51
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java42
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java17
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java80
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java81
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.java33
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java36
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java58
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java111
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java92
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java60
182 files changed, 22806 insertions, 0 deletions
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 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
2<html>
3<!--
4 Copyright (c) 2017, Eclipse.org Foundation, Inc.
5
6 SPDX-License-Identifier: LicenseRef-EPL-Steward
7-->
8<head>
9<title>About</title>
10<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
11</head>
12<body lang="EN-US">
13<h2>About This Content</h2>
14
15<p>March 18, 2019</p>
16<h3>License</h3>
17
18<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
19Eclipse Public License Version 2.0 (&quot;EPL&quot;). A copy of the EPL is available at <a href="http://www.eclipse.org/org/documents/epl-v20.php">http://www.eclipse.org/legal/epl-v20.html</a>.
20For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
21
22<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
23apply 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
24indicated 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 <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
25</body>
26</html>
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 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6
7plugins {
8 id("tools.refinery.gradle.java-library")
9}
10
11dependencies {
12 implementation(project(":refinery-viatra-runtime"))
13 implementation(project(":refinery-viatra-runtime-rete-recipes"))
14 implementation(libs.slf4j.log4j)
15}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Objects;
16
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
22import tools.refinery.viatra.runtime.matchers.util.Clearable;
23import tools.refinery.viatra.runtime.matchers.util.Direction;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
25import tools.refinery.viatra.runtime.rete.index.Indexer;
26import tools.refinery.viatra.runtime.rete.index.StandardIndexer;
27import tools.refinery.viatra.runtime.rete.network.Node;
28import tools.refinery.viatra.runtime.rete.network.Receiver;
29import tools.refinery.viatra.runtime.rete.network.ReteContainer;
30import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
31import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
32import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
33
34/**
35 * Groups incoming tuples by the given mask, and aggregates values at a specific index in each group.
36 * <p>
37 * Direct children are not supported, use via outer join indexers instead.
38 * <p>
39 * There are both timeless and timely implementations.
40 *
41 * @author Tamas Szabo
42 * @since 2.2
43 *
44 */
45public abstract class AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult> extends SingleInputNode
46 implements Clearable, IAggregatorNode {
47
48 /**
49 * @since 1.6
50 */
51 protected final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator;
52
53 /**
54 * @since 1.6
55 */
56 protected final TupleMask groupMask;
57
58 /**
59 * @since 1.6
60 */
61 protected final TupleMask columnMask;
62
63 /**
64 * @since 1.6
65 */
66 protected final int sourceWidth;
67
68 /**
69 * @since 1.6
70 */
71 protected final IQueryRuntimeContext runtimeContext;
72
73 protected final AggregateResult NEUTRAL;
74
75 protected AggregatorOuterIndexer aggregatorOuterIndexer;
76
77 @SuppressWarnings("rawtypes")
78 protected AbstractColumnAggregatorNode.AggregatorOuterIdentityIndexer[] aggregatorOuterIdentityIndexers;
79
80 /**
81 * Creates a new column aggregator node.
82 *
83 * @param reteContainer
84 * the RETE container of the node
85 * @param operator
86 * the aggregation operator
87 * @param deleteRederiveEvaluation
88 * true if the node should run in DRED mode, false otherwise
89 * @param groupMask
90 * the mask that masks a tuple to obtain the key that we are grouping-by
91 * @param columnMask
92 * the mask that masks a tuple to obtain the tuple element(s) that we are aggregating over
93 * @param posetComparator
94 * the poset comparator for the column, if known, otherwise it can be null
95 * @since 1.6
96 */
97 public AbstractColumnAggregatorNode(final ReteContainer reteContainer,
98 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
99 final TupleMask groupMask, final TupleMask columnMask) {
100 super(reteContainer);
101 this.operator = operator;
102 this.groupMask = groupMask;
103 this.columnMask = columnMask;
104 this.sourceWidth = groupMask.indices.length;
105 this.runtimeContext = reteContainer.getNetwork().getEngine().getRuntimeContext();
106 this.NEUTRAL = operator.getAggregate(operator.createNeutral());
107 reteContainer.registerClearable(this);
108 }
109
110 /**
111 * Creates a new column aggregator node.
112 *
113 * @param reteContainer
114 * the RETE container of the node
115 * @param operator
116 * the aggregation operator
117 * @param groupMask
118 * the mask that masks a tuple to obtain the key that we are grouping-by
119 * @param aggregatedColumn
120 * the index of the column that the aggregator node is aggregating over
121 */
122 public AbstractColumnAggregatorNode(final ReteContainer reteContainer,
123 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
124 final TupleMask groupMask, final int aggregatedColumn) {
125 this(reteContainer, operator, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth));
126 }
127
128 @Override
129 public CommunicationTracker getCommunicationTracker() {
130 return this.reteContainer.getCommunicationTracker();
131 }
132
133 @Override
134 public void pullInto(Collection<Tuple> collector, boolean flush) {
135 // DIRECT CHILDREN NOT SUPPORTED
136 throw new UnsupportedOperationException();
137 }
138
139 @Override
140 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
141 // DIRECT CHILDREN NOT SUPPORTED
142 throw new UnsupportedOperationException();
143 }
144
145 @Override
146 public void appendChild(Receiver receiver) {
147 // DIRECT CHILDREN NOT SUPPORTED
148 throw new UnsupportedOperationException();
149 }
150
151 @Override
152 public Indexer getAggregatorOuterIndexer() {
153 if (aggregatorOuterIndexer == null) {
154 aggregatorOuterIndexer = new AggregatorOuterIndexer();
155 this.getCommunicationTracker().registerDependency(this, aggregatorOuterIndexer);
156 }
157 return aggregatorOuterIndexer;
158 }
159
160 @Override
161 public Indexer getAggregatorOuterIdentityIndexer(final int resultPositionInSignature) {
162 if (aggregatorOuterIdentityIndexers == null) {
163 aggregatorOuterIdentityIndexers = new AbstractColumnAggregatorNode.AggregatorOuterIdentityIndexer[sourceWidth
164 + 1];
165 }
166 if (aggregatorOuterIdentityIndexers[resultPositionInSignature] == null) {
167 aggregatorOuterIdentityIndexers[resultPositionInSignature] = new AggregatorOuterIdentityIndexer(
168 resultPositionInSignature);
169 this.getCommunicationTracker().registerDependency(this,
170 aggregatorOuterIdentityIndexers[resultPositionInSignature]);
171 }
172 return aggregatorOuterIdentityIndexers[resultPositionInSignature];
173 }
174
175 /**
176 * @since 2.4
177 */
178 public void propagateAggregateResultUpdate(final Tuple group, final AggregateResult oldValue,
179 final AggregateResult newValue, final Timestamp timestamp) {
180 if (!Objects.equals(oldValue, newValue)) {
181 propagate(Direction.DELETE, group, oldValue, timestamp);
182 propagate(Direction.INSERT, group, newValue, timestamp);
183 }
184 }
185
186 /**
187 * @since 2.4
188 */
189 @SuppressWarnings("unchecked")
190 public void propagate(final Direction direction, final Tuple group, final AggregateResult value,
191 final Timestamp timestamp) {
192 final Tuple tuple = tupleFromAggregateResult(group, value);
193
194 if (aggregatorOuterIndexer != null) {
195 aggregatorOuterIndexer.propagate(direction, tuple, group, timestamp);
196 }
197 if (aggregatorOuterIdentityIndexers != null) {
198 for (final AggregatorOuterIdentityIndexer aggregatorOuterIdentityIndexer : aggregatorOuterIdentityIndexers) {
199 if (aggregatorOuterIdentityIndexer != null) {
200 aggregatorOuterIdentityIndexer.propagate(direction, tuple, group, timestamp);
201 }
202 }
203 }
204 }
205
206 public abstract Tuple getAggregateTuple(final Tuple key);
207
208 /**
209 * @since 2.4
210 */
211 public abstract Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(final Tuple key);
212
213 public abstract AggregateResult getAggregateResult(final Tuple key);
214
215 /**
216 * @since 2.4
217 */
218 public abstract Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(final Tuple key);
219
220 protected Tuple tupleFromAggregateResult(final Tuple groupTuple, final AggregateResult aggregateResult) {
221 if (aggregateResult == null) {
222 return null;
223 } else {
224 return Tuples.staticArityLeftInheritanceTupleOf(groupTuple, runtimeContext.wrapElement(aggregateResult));
225 }
226 }
227
228 /**
229 * A special non-iterable index that retrieves the aggregated, packed result (signature+aggregate) for the original
230 * signature.
231 *
232 * @author Gabor Bergmann
233 * @author Tamas Szabo
234 *
235 */
236 protected class AggregatorOuterIndexer extends StandardIndexer {
237
238 /**
239 * @since 2.4
240 */
241 protected NetworkStructureChangeSensitiveLogic logic;
242
243 public AggregatorOuterIndexer() {
244 super(AbstractColumnAggregatorNode.this.reteContainer, TupleMask.omit(sourceWidth, sourceWidth + 1));
245 this.parent = AbstractColumnAggregatorNode.this;
246 this.logic = createLogic();
247 }
248
249 @Override
250 public void networkStructureChanged() {
251 super.networkStructureChanged();
252 this.logic = createLogic();
253 }
254
255 @Override
256 public Collection<Tuple> get(final Tuple signature) {
257 return this.logic.get(signature);
258 }
259
260 @Override
261 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
262 return this.logic.getTimeline(signature);
263 }
264
265 /**
266 * @since 2.4
267 */
268 public void propagate(final Direction direction, final Tuple tuple, final Tuple group,
269 final Timestamp timestamp) {
270 if (tuple != null) {
271 propagate(direction, tuple, group, true, timestamp);
272 }
273 }
274
275 @Override
276 public Node getActiveNode() {
277 return AbstractColumnAggregatorNode.this;
278 }
279
280 /**
281 * @since 2.4
282 */
283 protected NetworkStructureChangeSensitiveLogic createLogic() {
284 if (this.reteContainer.isTimelyEvaluation()
285 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
286 return this.TIMELY;
287 } else {
288 return this.TIMELESS;
289 }
290 }
291
292 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
293
294 @Override
295 public Collection<Tuple> get(final Tuple signature) {
296 final Tuple aggregateTuple = getAggregateTuple(signature);
297 if (aggregateTuple == null) {
298 return null;
299 } else {
300 return Collections.singleton(aggregateTuple);
301 }
302 }
303
304 @Override
305 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
306 throw new UnsupportedOperationException();
307 }
308
309 };
310
311 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
312
313 @Override
314 public Collection<Tuple> get(final Tuple signatureWithResult) {
315 return TIMELESS.get(signatureWithResult);
316 }
317
318 @Override
319 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
320 final Map<Tuple, Timeline<Timestamp>> aggregateTuples = getAggregateTupleTimeline(signature);
321 if (aggregateTuples.isEmpty()) {
322 return null;
323 } else {
324 return aggregateTuples;
325 }
326 }
327
328 };
329
330 }
331
332 /**
333 * A special non-iterable index that checks a suspected aggregate value for a given signature. The signature for
334 * this index is the original 'group by' masked tuple, with the suspected result inserted at position
335 * resultPositionInSignature.
336 *
337 * @author Gabor Bergmann
338 * @author Tamas Szabo
339 *
340 */
341 protected class AggregatorOuterIdentityIndexer extends StandardIndexer {
342
343 protected final int resultPositionInSignature;
344 protected final TupleMask pruneResult;
345 protected final TupleMask reorderMask;
346 /**
347 * @since 2.4
348 */
349 protected NetworkStructureChangeSensitiveLogic logic;
350
351 public AggregatorOuterIdentityIndexer(final int resultPositionInSignature) {
352 super(AbstractColumnAggregatorNode.this.reteContainer,
353 TupleMask.displace(sourceWidth, resultPositionInSignature, sourceWidth + 1));
354 this.resultPositionInSignature = resultPositionInSignature;
355 this.pruneResult = TupleMask.omit(resultPositionInSignature, sourceWidth + 1);
356 if (resultPositionInSignature == sourceWidth) {
357 this.reorderMask = null;
358 } else {
359 this.reorderMask = mask;
360 }
361 this.logic = createLogic();
362 }
363
364 @Override
365 public void networkStructureChanged() {
366 super.networkStructureChanged();
367 this.logic = createLogic();
368 }
369
370 @Override
371 public Collection<Tuple> get(final Tuple signatureWithResult) {
372 return this.logic.get(signatureWithResult);
373 }
374
375 /**
376 * @since 2.4
377 */
378 @Override
379 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
380 return this.logic.getTimeline(signature);
381 }
382
383 /**
384 * @since 2.4
385 */
386 public void propagate(final Direction direction, final Tuple tuple, final Tuple group,
387 final Timestamp timestamp) {
388 if (tuple != null) {
389 propagate(direction, reorder(tuple), group, true, timestamp);
390 }
391 }
392
393 private Tuple reorder(final Tuple signatureWithResult) {
394 Tuple transformed;
395 if (reorderMask == null) {
396 transformed = signatureWithResult;
397 } else {
398 transformed = reorderMask.transform(signatureWithResult);
399 }
400 return transformed;
401 }
402
403 @Override
404 public Node getActiveNode() {
405 return this.parent;
406 }
407
408 /**
409 * @since 2.4
410 */
411 protected NetworkStructureChangeSensitiveLogic createLogic() {
412 if (this.reteContainer.isTimelyEvaluation()
413 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
414 return this.TIMELY;
415 } else {
416 return this.TIMELESS;
417 }
418 }
419
420 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
421
422 @Override
423 public Collection<Tuple> get(final Tuple signatureWithResult) {
424 final Tuple prunedSignature = pruneResult.transform(signatureWithResult);
425 final AggregateResult result = getAggregateResult(prunedSignature);
426 if (result != null && Objects.equals(signatureWithResult.get(resultPositionInSignature), result)) {
427 return Collections.singleton(signatureWithResult);
428 } else {
429 return null;
430 }
431 }
432
433 @Override
434 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
435 throw new UnsupportedOperationException();
436 }
437
438 };
439
440 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
441
442 @Override
443 public Collection<Tuple> get(final Tuple signatureWithResult) {
444 return TIMELESS.get(signatureWithResult);
445 }
446
447 @Override
448 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signatureWithResult) {
449 final Tuple prunedSignature = pruneResult.transform(signatureWithResult);
450 final Map<AggregateResult, Timeline<Timestamp>> result = getAggregateResultTimeline(prunedSignature);
451 for (final Entry<AggregateResult, Timeline<Timestamp>> entry : result.entrySet()) {
452 if (Objects.equals(signatureWithResult.get(resultPositionInSignature), entry.getKey())) {
453 return Collections.singletonMap(signatureWithResult, entry.getValue());
454 }
455 }
456 return null;
457 }
458
459 };
460
461 }
462
463 /**
464 * @since 2.4
465 */
466 protected static abstract class NetworkStructureChangeSensitiveLogic {
467
468 public abstract Collection<Tuple> get(final Tuple signatureWithResult);
469
470 public abstract Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature);
471
472 }
473
474}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation;
10
11import java.util.Map;
12import java.util.Map.Entry;
13
14import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
15import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
21import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver;
22import tools.refinery.viatra.runtime.rete.network.RederivableNode;
23import tools.refinery.viatra.runtime.rete.network.ReteContainer;
24import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.communication.timeless.RecursiveCommunicationGroup;
27import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
28import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
29import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.PosetAwareMailbox;
30
31/**
32 * Timeless implementation of the column aggregator node.
33 * <p>
34 * The node is capable of operating in the delete and re-derive mode. In this mode, it is also possible to equip the
35 * node with an {@link IPosetComparator} to identify monotone changes; thus, ensuring that a fix-point can be reached
36 * during the evaluation.
37 *
38 * @author Gabor Bergmann
39 * @author Tamas Szabo
40 * @since 1.4
41 */
42public class ColumnAggregatorNode<Domain, Accumulator, AggregateResult>
43 extends AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult>
44 implements RederivableNode, PosetAwareReceiver {
45
46 /**
47 * @since 1.6
48 */
49 protected final IPosetComparator posetComparator;
50
51 /**
52 * @since 1.6
53 */
54 protected final boolean deleteRederiveEvaluation;
55
56 // invariant: neutral values are not stored
57 /**
58 * @since 1.6
59 */
60 protected final Map<Tuple, Accumulator> memory;
61 /**
62 * @since 1.6
63 */
64 protected final Map<Tuple, Accumulator> rederivableMemory;
65
66 /**
67 * @since 1.7
68 */
69 protected CommunicationGroup currentGroup;
70
71 /**
72 * Creates a new column aggregator node.
73 *
74 * @param reteContainer
75 * the RETE container of the node
76 * @param operator
77 * the aggregation operator
78 * @param deleteRederiveEvaluation
79 * true if the node should run in DRED mode, false otherwise
80 * @param groupMask
81 * the mask that masks a tuple to obtain the key that we are grouping-by
82 * @param columnMask
83 * the mask that masks a tuple to obtain the tuple element(s) that we are aggregating over
84 * @param posetComparator
85 * the poset comparator for the column, if known, otherwise it can be null
86 * @since 1.6
87 */
88 public ColumnAggregatorNode(final ReteContainer reteContainer,
89 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
90 final boolean deleteRederiveEvaluation, final TupleMask groupMask, final TupleMask columnMask,
91 final IPosetComparator posetComparator) {
92 super(reteContainer, operator, groupMask, columnMask);
93 this.memory = CollectionsFactory.createMap();
94 this.rederivableMemory = CollectionsFactory.createMap();
95 this.deleteRederiveEvaluation = deleteRederiveEvaluation;
96 this.posetComparator = posetComparator;
97 // mailbox MUST be instantiated after the fields are all set
98 this.mailbox = instantiateMailbox();
99 }
100
101 /**
102 * Creates a new column aggregator node.
103 *
104 * @param reteContainer
105 * the RETE container of the node
106 * @param operator
107 * the aggregation operator
108 * @param groupMask
109 * the mask that masks a tuple to obtain the key that we are grouping-by
110 * @param aggregatedColumn
111 * the index of the column that the aggregator node is aggregating over
112 */
113 public ColumnAggregatorNode(final ReteContainer reteContainer,
114 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
115 final TupleMask groupMask, final int aggregatedColumn) {
116 this(reteContainer, operator, false, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth),
117 null);
118 }
119
120 @Override
121 public boolean isInDRedMode() {
122 return this.deleteRederiveEvaluation;
123 }
124
125 @Override
126 protected Mailbox instantiateMailbox() {
127 if (groupMask != null && columnMask != null && posetComparator != null) {
128 return new PosetAwareMailbox(this, this.reteContainer);
129 } else {
130 return new BehaviorChangingMailbox(this, this.reteContainer);
131 }
132 }
133
134 @Override
135 public TupleMask getCoreMask() {
136 return groupMask;
137 }
138
139 @Override
140 public TupleMask getPosetMask() {
141 return columnMask;
142 }
143
144 @Override
145 public IPosetComparator getPosetComparator() {
146 return posetComparator;
147 }
148
149 @Override
150 public void rederiveOne() {
151 final Entry<Tuple, Accumulator> entry = rederivableMemory.entrySet().iterator().next();
152 final Tuple group = entry.getKey();
153 final Accumulator accumulator = entry.getValue();
154 rederivableMemory.remove(group);
155 memory.put(group, accumulator);
156 // unregister the node if there is nothing left to be re-derived
157 if (this.rederivableMemory.isEmpty()) {
158 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
159 }
160 final AggregateResult value = operator.getAggregate(accumulator);
161 propagateAggregateResultUpdate(group, NEUTRAL, value, Timestamp.ZERO);
162 }
163
164 @Override
165 public void updateWithPosetInfo(final Direction direction, final Tuple update, final boolean monotone) {
166 if (this.deleteRederiveEvaluation) {
167 updateWithDeleteAndRederive(direction, update, monotone);
168 } else {
169 updateDefault(direction, update, Timestamp.ZERO);
170 }
171 }
172
173 @Override
174 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
175 updateWithPosetInfo(direction, update, false);
176 }
177
178 /**
179 * @since 2.4
180 */
181 protected void updateDefault(final Direction direction, final Tuple update, final Timestamp timestamp) {
182 final Tuple key = groupMask.transform(update);
183 final Tuple value = columnMask.transform(update);
184 @SuppressWarnings("unchecked")
185 final Domain aggregableValue = (Domain) runtimeContext.unwrapElement(value.get(0));
186 final boolean isInsertion = direction == Direction.INSERT;
187
188 final Accumulator oldMainAccumulator = getMainAccumulator(key);
189 final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator);
190
191 final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue, isInsertion);
192 storeIfNotNeutral(key, newMainAccumulator, memory);
193 final AggregateResult newValue = operator.getAggregate(newMainAccumulator);
194
195 propagateAggregateResultUpdate(key, oldValue, newValue, timestamp);
196 }
197
198 /**
199 * @since 2.4
200 */
201 protected void updateWithDeleteAndRederive(final Direction direction, final Tuple update, final boolean monotone) {
202 final Tuple group = groupMask.transform(update);
203 final Tuple value = columnMask.transform(update);
204 @SuppressWarnings("unchecked")
205 final Domain aggregableValue = (Domain) runtimeContext.unwrapElement(value.get(0));
206 final boolean isInsertion = direction == Direction.INSERT;
207
208 Accumulator oldMainAccumulator = memory.get(group);
209 Accumulator oldRederivableAccumulator = rederivableMemory.get(group);
210
211 if (direction == Direction.INSERT) {
212 // INSERT
213 if (oldRederivableAccumulator != null) {
214 // the group is in the re-derivable memory
215 final Accumulator newRederivableAccumulator = operator.update(oldRederivableAccumulator,
216 aggregableValue, isInsertion);
217 storeIfNotNeutral(group, newRederivableAccumulator, rederivableMemory);
218 if (rederivableMemory.isEmpty()) {
219 // there is nothing left to be re-derived
220 // this can happen if the accumulator became neutral in response to the INSERT
221 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
222 }
223 } else {
224 // the group is in the main memory
225 // at this point, it can happen that we need to initialize with a neutral accumulator
226 if (oldMainAccumulator == null) {
227 oldMainAccumulator = operator.createNeutral();
228 }
229
230 final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator);
231 final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue,
232 isInsertion);
233 storeIfNotNeutral(group, newMainAccumulator, memory);
234 final AggregateResult newValue = operator.getAggregate(newMainAccumulator);
235 propagateAggregateResultUpdate(group, oldValue, newValue, Timestamp.ZERO);
236 }
237 } else {
238 // DELETE
239 if (oldRederivableAccumulator != null) {
240 // the group is in the re-derivable memory
241 if (oldMainAccumulator != null) {
242 issueError("[INTERNAL ERROR] Inconsistent state for " + update
243 + " because it is present both in the main and re-derivable memory in the ColumnAggregatorNode "
244 + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(), null);
245 }
246 try {
247 final Accumulator newRederivableAccumulator = operator.update(oldRederivableAccumulator,
248 aggregableValue, isInsertion);
249 storeIfNotNeutral(group, newRederivableAccumulator, rederivableMemory);
250 if (rederivableMemory.isEmpty()) {
251 // there is nothing left to be re-derived
252 // this can happen if the accumulator became neutral in response to the DELETE
253 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
254 }
255 } catch (final NullPointerException ex) {
256 issueError("[INTERNAL ERROR] Deleting a domain element in " + update
257 + " which did not exist before in ColumnAggregatorNode " + this + " for pattern(s) "
258 + getTraceInfoPatternsEnumerated(), ex);
259 }
260 } else {
261 // the group is in the main memory
262 // at this point, it can happen that we need to initialize with a neutral accumulator
263 if (oldMainAccumulator == null) {
264 oldMainAccumulator = operator.createNeutral();
265 }
266
267 final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator);
268 final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue,
269 isInsertion);
270 final AggregateResult newValue = operator.getAggregate(newMainAccumulator);
271
272 if (monotone) {
273 storeIfNotNeutral(group, newMainAccumulator, memory);
274 propagateAggregateResultUpdate(group, oldValue, newValue, Timestamp.ZERO);
275 } else {
276 final boolean wasEmpty = rederivableMemory.isEmpty();
277 if (storeIfNotNeutral(group, newMainAccumulator, rederivableMemory) && wasEmpty) {
278 ((RecursiveCommunicationGroup) currentGroup).addRederivable(this);
279 }
280 memory.remove(group);
281 propagateAggregateResultUpdate(group, oldValue, NEUTRAL, Timestamp.ZERO);
282 }
283 }
284 }
285 }
286
287 @Override
288 public void clear() {
289 this.memory.clear();
290 this.rederivableMemory.clear();
291 this.childMailboxes.clear();
292 }
293
294 /**
295 * Returns true if the accumulator was stored, false otherwise.
296 *
297 * @since 1.6
298 */
299 protected boolean storeIfNotNeutral(final Tuple key, final Accumulator accumulator,
300 final Map<Tuple, Accumulator> memory) {
301 if (operator.isNeutral(accumulator)) {
302 memory.remove(key);
303 return false;
304 } else {
305 memory.put(key, accumulator);
306 return true;
307 }
308 }
309
310 @Override
311 public Tuple getAggregateTuple(final Tuple group) {
312 final Accumulator accumulator = getMainAccumulator(group);
313 final AggregateResult result = operator.getAggregate(accumulator);
314 return tupleFromAggregateResult(group, result);
315 }
316
317 @Override
318 public AggregateResult getAggregateResult(final Tuple group) {
319 final Accumulator accumulator = getMainAccumulator(group);
320 return operator.getAggregate(accumulator);
321 }
322
323 @Override
324 public Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(Tuple key) {
325 throw new UnsupportedOperationException();
326 }
327
328 @Override
329 public Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(Tuple key) {
330 throw new UnsupportedOperationException();
331 }
332
333 /**
334 * @since 1.6
335 */
336 protected Accumulator getMainAccumulator(final Tuple key) {
337 return getAccumulator(key, memory);
338 }
339
340 /**
341 * @since 1.6
342 */
343 protected Accumulator getRederivableAccumulator(final Tuple key) {
344 return getAccumulator(key, rederivableMemory);
345 }
346
347 /**
348 * @since 1.6
349 */
350 protected Accumulator getAccumulator(final Tuple key, final Map<Tuple, Accumulator> memory) {
351 Accumulator accumulator = memory.get(key);
352 if (accumulator == null) {
353 return operator.createNeutral();
354 } else {
355 return accumulator;
356 }
357 }
358
359 @Override
360 public CommunicationGroup getCurrentGroup() {
361 return currentGroup;
362 }
363
364 @Override
365 public void setCurrentGroup(final CommunicationGroup currentGroup) {
366 this.currentGroup = currentGroup;
367 }
368
369}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.aggregation;
11
12import java.util.Collection;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.rete.network.ReteContainer;
16
17/**
18 * An aggregation node that simply counts the number of tuples conforming to the signature.
19 *
20 * @author Gabor Bergmann
21 * @since 1.4
22 */
23public class CountNode extends IndexerBasedAggregatorNode {
24
25 public CountNode(ReteContainer reteContainer) {
26 super(reteContainer);
27 }
28
29 int sizeOf(Collection<Tuple> group) {
30 return group == null ? 0 : group.size();
31 }
32
33 @Override
34 public Object aggregateGroup(Tuple signature, Collection<Tuple> group) {
35 return sizeOf(group);
36 }
37
38}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation;
10
11import java.util.AbstractMap.SimpleEntry;
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
19
20/**
21 * An optimized {@link Map} implementation where each key is produced by joining together a group tuple and some other
22 * object (via left inheritance). Only a select few {@link Map} operations are supported. This collection is
23 * unmodifiable.
24 *
25 * Operations on this map assume that client queries also obey the contract that keys are constructed from a group tuple
26 * and an additional object.
27 *
28 * @author Tamas Szabo
29 * @since 2.4
30 */
31public class GroupedMap<GroupedKeyType, ValueType> implements Map<Tuple, ValueType> {
32
33 protected final Tuple group;
34 // cached group size value is to be used in get()
35 private final int groupSize;
36 protected final Map<GroupedKeyType, ValueType> mappings;
37 protected final IQueryRuntimeContext runtimeContext;
38
39 public GroupedMap(final Tuple group, final Map<GroupedKeyType, ValueType> mappings,
40 final IQueryRuntimeContext runtimeContext) {
41 this.group = group;
42 this.groupSize = group.getSize();
43 this.mappings = mappings;
44 this.runtimeContext = runtimeContext;
45 }
46
47 @Override
48 public int size() {
49 return this.mappings.size();
50 }
51
52 @Override
53 public boolean isEmpty() {
54 return this.mappings.isEmpty();
55 }
56
57 @Override
58 public boolean containsKey(final Object key) {
59 throw new UnsupportedOperationException();
60 }
61
62 @Override
63 public boolean containsValue(final Object value) {
64 return this.mappings.containsValue(value);
65 }
66
67 @Override
68 public ValueType get(final Object key) {
69 if (key instanceof Tuple) {
70 final Object value = ((Tuple) key).get(this.groupSize);
71 final Object unwrappedValue = this.runtimeContext.unwrapElement(value);
72 return this.mappings.get(unwrappedValue);
73 } else {
74 return null;
75 }
76 }
77
78 @Override
79 public ValueType put(final Tuple key, final ValueType value) {
80 throw new UnsupportedOperationException();
81 }
82
83 @Override
84 public ValueType remove(final Object key) {
85 throw new UnsupportedOperationException();
86 }
87
88 @Override
89 public void putAll(final Map<? extends Tuple, ? extends ValueType> map) {
90 throw new UnsupportedOperationException();
91 }
92
93 @Override
94 public void clear() {
95 throw new UnsupportedOperationException();
96 }
97
98 @Override
99 public Set<Tuple> keySet() {
100 return new GroupedSet<Tuple, GroupedKeyType, Tuple>(this.group, this.mappings.keySet(), (g, v) -> {
101 return Tuples.staticArityLeftInheritanceTupleOf(g, this.runtimeContext.wrapElement(v));
102 });
103 }
104
105 @Override
106 public Collection<ValueType> values() {
107 return this.mappings.values();
108 }
109
110 @Override
111 public Set<Entry<Tuple, ValueType>> entrySet() {
112 return new GroupedSet<Tuple, GroupedKeyType, Entry<Tuple, ValueType>>(this.group, this.mappings.keySet(),
113 (g, v) -> {
114 final Tuple key = Tuples.staticArityLeftInheritanceTupleOf(g, this.runtimeContext.wrapElement(v));
115 final ValueType value = this.mappings.get(v);
116 return new SimpleEntry<Tuple, ValueType>(key, value);
117 });
118 }
119
120} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation;
10
11import java.util.Collection;
12import java.util.Iterator;
13import java.util.Set;
14import java.util.function.BiFunction;
15
16/**
17 * An optimized {@link Set} implementation where each contained value is produced by combining together a grouping value
18 * and some other (key) object. The way of combining together these two values is specified by the closure passed to the
19 * constructor. Only a select few {@link Set} operations are supported. This collection is unmodifiable.
20 *
21 * @author Tamas Szabo
22 * @since 2.4
23 */
24public class GroupedSet<GroupingValueType, GroupedKeyType, WholeKeyType> implements Set<WholeKeyType> {
25
26 protected final GroupingValueType group;
27 protected final Collection<GroupedKeyType> values;
28 protected final BiFunction<GroupingValueType, GroupedKeyType, WholeKeyType> valueFunc;
29
30 public GroupedSet(final GroupingValueType group, final Collection<GroupedKeyType> values,
31 final BiFunction<GroupingValueType, GroupedKeyType, WholeKeyType> valueFunc) {
32 this.group = group;
33 this.values = values;
34 this.valueFunc = valueFunc;
35 }
36
37 @Override
38 public int size() {
39 return this.values.size();
40 }
41
42 @Override
43 public boolean isEmpty() {
44 return this.values.isEmpty();
45 }
46
47 @Override
48 public boolean contains(final Object obj) {
49 throw new UnsupportedOperationException();
50 }
51
52 @Override
53 public Iterator<WholeKeyType> iterator() {
54 final Iterator<GroupedKeyType> wrapped = this.values.iterator();
55 return new Iterator<WholeKeyType>() {
56 @Override
57 public boolean hasNext() {
58 return wrapped.hasNext();
59 }
60
61 @Override
62 public WholeKeyType next() {
63 final GroupedKeyType value = wrapped.next();
64 return valueFunc.apply(group, value);
65 }
66 };
67 }
68
69 @Override
70 public Object[] toArray() {
71 throw new UnsupportedOperationException();
72 }
73
74 @Override
75 public <T> T[] toArray(final T[] arr) {
76 throw new UnsupportedOperationException();
77 }
78
79 @Override
80 public boolean add(final WholeKeyType tuple) {
81 throw new UnsupportedOperationException();
82 }
83
84 @Override
85 public boolean remove(final Object obj) {
86 throw new UnsupportedOperationException();
87 }
88
89 @Override
90 public boolean containsAll(final Collection<?> c) {
91 throw new UnsupportedOperationException();
92 }
93
94 @Override
95 public boolean addAll(final Collection<? extends WholeKeyType> coll) {
96 throw new UnsupportedOperationException();
97 }
98
99 @Override
100 public boolean retainAll(final Collection<?> coll) {
101 throw new UnsupportedOperationException();
102 }
103
104 @Override
105 public boolean removeAll(final Collection<?> coll) {
106 throw new UnsupportedOperationException();
107 }
108
109 @Override
110 public void clear() {
111 throw new UnsupportedOperationException();
112 }
113
114} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation;
10
11import tools.refinery.viatra.runtime.rete.index.Indexer;
12
13/**
14 * Expresses that aggregators expose specialized non-enumerable indexers for outer joining.
15 * @author Gabor Bergmann
16 *
17 * @since 1.4
18 *
19 */
20public interface IAggregatorNode {
21
22 Indexer getAggregatorOuterIndexer();
23
24 Indexer getAggregatorOuterIdentityIndexer(int resultPositionInSignature);
25
26} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.aggregation;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Direction;
22import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
23import tools.refinery.viatra.runtime.rete.index.DefaultIndexerListener;
24import tools.refinery.viatra.runtime.rete.index.Indexer;
25import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
26import tools.refinery.viatra.runtime.rete.index.StandardIndexer;
27import tools.refinery.viatra.runtime.rete.network.Node;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.StandardNode;
30import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
31import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
32
33/**
34 * A special node depending on a projection indexer to aggregate tuple groups with the same projection. Only propagates
35 * the aggregates of non-empty groups. Use the outer indexers to circumvent.
36 * <p>
37 * This node cannot be used in recursive differential dataflow evaluation.
38 *
39 * @author Gabor Bergmann
40 * @since 1.4
41 */
42public abstract class IndexerBasedAggregatorNode extends StandardNode implements IAggregatorNode {
43
44 ProjectionIndexer projection;
45 IndexerBasedAggregatorNode me;
46 int sourceWidth;
47 Map<Tuple, Object> mainAggregates;
48
49 AggregatorOuterIndexer aggregatorOuterIndexer = null;
50 AggregatorOuterIdentityIndexer[] aggregatorOuterIdentityIndexers = null;
51
52 /**
53 * MUST call initializeWith() afterwards!
54 */
55 public IndexerBasedAggregatorNode(ReteContainer reteContainer) {
56 super(reteContainer);
57 this.me = this;
58 mainAggregates = CollectionsFactory.createMap();
59 }
60
61 @Override
62 public void networkStructureChanged() {
63 if (this.reteContainer.isTimelyEvaluation() && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
64 throw new IllegalStateException(this.toString() + " cannot be used in recursive differential dataflow evaluation!");
65 }
66 super.networkStructureChanged();
67 }
68
69 /**
70 * @param projection
71 * the projection indexer whose tuple groups should be aggregated
72 */
73 public void initializeWith(ProjectionIndexer projection) {
74 this.projection = projection;
75 this.sourceWidth = projection.getMask().indices.length;
76
77 for (Tuple signature : projection.getSignatures()) {
78 mainAggregates.put(signature, aggregateGroup(signature, projection.get(signature)));
79 }
80 projection.attachListener(new DefaultIndexerListener(this) {
81 @Override
82 public void notifyIndexerUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp) {
83 aggregateUpdate(direction, updateElement, signature, change);
84 }
85 });
86 }
87
88 /**
89 * Aggregates (reduces) a group of tuples. The group can be null.
90 */
91 public abstract Object aggregateGroup(Tuple signature, Collection<Tuple> group);
92
93
94 /**
95 * Aggregates (reduces) a group of tuples, having access to the previous aggregated value (before the update) and
96 * the update definition. Defaults to aggregateGroup(). Override to increase performance.
97 * @since 2.4
98 */
99 public Object aggregateGroupAfterUpdate(Tuple signature, Collection<Tuple> currentGroup, Object oldAggregate,
100 Direction direction, Tuple updateElement, boolean change) {
101 return aggregateGroup(signature, currentGroup);
102 }
103
104 protected Tuple aggregateAndPack(Tuple signature, Collection<Tuple> group) {
105 return packResult(signature, aggregateGroup(signature, group));
106 }
107
108 @Override
109 public Indexer getAggregatorOuterIndexer() {
110 if (aggregatorOuterIndexer == null) {
111 aggregatorOuterIndexer = new AggregatorOuterIndexer();
112 this.getCommunicationTracker().registerDependency(this, aggregatorOuterIndexer);
113 // reteContainer.connectAndSynchronize(this, aggregatorOuterIndexer);
114 }
115 return aggregatorOuterIndexer;
116 }
117
118 @Override
119 public Indexer getAggregatorOuterIdentityIndexer(int resultPositionInSignature) {
120 if (aggregatorOuterIdentityIndexers == null)
121 aggregatorOuterIdentityIndexers = new AggregatorOuterIdentityIndexer[sourceWidth + 1];
122 if (aggregatorOuterIdentityIndexers[resultPositionInSignature] == null) {
123 aggregatorOuterIdentityIndexers[resultPositionInSignature] = new AggregatorOuterIdentityIndexer(
124 resultPositionInSignature);
125 this.getCommunicationTracker().registerDependency(this, aggregatorOuterIdentityIndexers[resultPositionInSignature]);
126 // reteContainer.connectAndSynchronize(this, aggregatorOuterIdentityIndexers[resultPositionInSignature]);
127 }
128 return aggregatorOuterIdentityIndexers[resultPositionInSignature];
129 }
130
131 @Override
132 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
133 for (final Entry<Tuple, Object> aggregateEntry : mainAggregates.entrySet()) {
134 collector.add(packResult(aggregateEntry.getKey(), aggregateEntry.getValue()));
135 }
136 }
137
138 @Override
139 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
140 // use all zero timestamps because this node cannot be used in recursive groups anyway
141 for (final Entry<Tuple, Object> aggregateEntry : mainAggregates.entrySet()) {
142 collector.put(packResult(aggregateEntry.getKey(), aggregateEntry.getValue()), Timestamp.INSERT_AT_ZERO_TIMELINE);
143 }
144 }
145
146 protected Tuple packResult(Tuple signature, Object result) {
147 return Tuples.staticArityLeftInheritanceTupleOf(signature, result);
148 }
149
150 /**
151 * @since 2.4
152 */
153 protected void aggregateUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change) {
154 Collection<Tuple> currentGroup = projection.get(signature);
155 // these will be null if group is empty
156 Object oldAggregate = mainAggregates.get(signature);
157 Object safeOldAggregate = oldAggregate == null ? aggregateGroup(signature, null) : oldAggregate;
158 boolean empty = currentGroup == null || currentGroup.isEmpty();
159 Object newAggregate = empty ? null : aggregateGroupAfterUpdate(signature, currentGroup, safeOldAggregate/*
160 * non-null
161 */,
162 direction, updateElement, change);
163 if (!empty)
164 mainAggregates.put(signature, newAggregate);
165 else
166 mainAggregates.remove(signature);
167 Tuple oldTuple = packResult(signature, safeOldAggregate);
168 Tuple newTuple = packResult(signature, newAggregate == null ? aggregateGroup(signature, null) : newAggregate);
169 if (oldAggregate != null)
170 propagateUpdate(Direction.DELETE, oldTuple, Timestamp.ZERO); // direct outputs lack non-empty groups
171 if (newAggregate != null)
172 propagateUpdate(Direction.INSERT, newTuple, Timestamp.ZERO); // direct outputs lack non-empty groups
173 if (aggregatorOuterIndexer != null)
174 aggregatorOuterIndexer.propagate(signature, oldTuple, newTuple);
175 if (aggregatorOuterIdentityIndexers != null)
176 for (AggregatorOuterIdentityIndexer aggregatorOuterIdentityIndexer : aggregatorOuterIdentityIndexers)
177 if (aggregatorOuterIdentityIndexer != null)
178 aggregatorOuterIdentityIndexer.propagate(signature, oldTuple, newTuple);
179 }
180
181 private Object getAggregate(Tuple signature) {
182 Object aggregate = mainAggregates.get(signature);
183 return aggregate == null ? aggregateGroup(signature, null) : aggregate;
184 }
185
186 @Override
187 public void assignTraceInfo(TraceInfo traceInfo) {
188 super.assignTraceInfo(traceInfo);
189 if (traceInfo.propagateToIndexerParent() && projection != null)
190 projection.acceptPropagatedTraceInfo(traceInfo);
191 }
192
193 /**
194 * A special non-iterable index that retrieves the aggregated, packed result (signature+aggregate) for the original
195 * signature.
196 *
197 * @author Gabor Bergmann
198 */
199 class AggregatorOuterIndexer extends StandardIndexer {
200
201 public AggregatorOuterIndexer() {
202 super(me.reteContainer, TupleMask.omit(sourceWidth, sourceWidth + 1));
203 this.parent = me;
204 }
205
206 @Override
207 public Collection<Tuple> get(Tuple signature) {
208 return Collections.singleton(packResult(signature, getAggregate(signature)));
209 }
210
211 public void propagate(Tuple signature, Tuple oldTuple, Tuple newTuple) {
212 propagate(Direction.INSERT, newTuple, signature, false, Timestamp.ZERO);
213 propagate(Direction.DELETE, oldTuple, signature, false, Timestamp.ZERO);
214 }
215
216 @Override
217 public Node getActiveNode() {
218 return projection.getActiveNode();
219 }
220
221 }
222
223 /**
224 * A special non-iterable index that checks a suspected aggregate value for a given signature. The signature for
225 * this index is the original signature of the projection index, with the suspected result inserted at position
226 * resultPositionInSignature.
227 *
228 * @author Gabor Bergmann
229 */
230
231 class AggregatorOuterIdentityIndexer extends StandardIndexer {
232 int resultPositionInSignature;
233 TupleMask pruneResult;
234 TupleMask reorderMask;
235
236 public AggregatorOuterIdentityIndexer(int resultPositionInSignature) {
237 super(me.reteContainer, TupleMask.displace(sourceWidth, resultPositionInSignature, sourceWidth + 1));
238 this.parent = me;
239 // this.localAggregates = new HashMap<Tuple, Tuple>();
240 this.resultPositionInSignature = resultPositionInSignature;
241 this.pruneResult = TupleMask.omit(resultPositionInSignature, sourceWidth + 1);
242 if (resultPositionInSignature == sourceWidth)
243 this.reorderMask = null;
244 else
245 this.reorderMask = mask;
246 }
247
248 @Override
249 public Collection<Tuple> get(Tuple signatureWithResult) {
250 Tuple prunedSignature = pruneResult.transform(signatureWithResult);
251 Object result = getAggregate(prunedSignature);
252 if (signatureWithResult.get(resultPositionInSignature).equals(result))
253 return Collections.singleton(signatureWithResult);
254 else
255 return null;
256 }
257
258 public void propagate(Tuple signature, Tuple oldTuple, Tuple newTuple) {
259 propagate(Direction.INSERT, reorder(newTuple), signature, true, Timestamp.ZERO);
260 propagate(Direction.DELETE, reorder(oldTuple), signature, true, Timestamp.ZERO);
261 }
262
263 private Tuple reorder(Tuple signatureWithResult) {
264 Tuple transformed;
265 if (reorderMask == null)
266 transformed = signatureWithResult;
267 else
268 transformed = reorderMask.transform(signatureWithResult);
269 return transformed;
270 }
271
272 @Override
273 public Node getActiveNode() {
274 return projection.getActiveNode();
275 }
276 }
277
278}
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..19e02f10
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java
@@ -0,0 +1,212 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation.timely;
10
11import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
14import tools.refinery.viatra.runtime.matchers.util.*;
15import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
16import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode.CumulativeAggregate;
17import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode.FoldingState;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
21
22import java.util.Collections;
23import java.util.Map;
24import java.util.Map.Entry;
25import java.util.Objects;
26import java.util.TreeMap;
27
28/**
29 * Faithful column aggregator with parallel aggregation architecture.
30 *
31 * @author Tamas Szabo
32 * @since 2.4
33 *
34 */
35public class FaithfulParallelTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> extends
36 FaithfulTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult, CumulativeAggregate<Domain, Accumulator>, FoldingState<Domain>>
37 implements ResumableNode {
38
39 public FaithfulParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer,
40 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
41 final TupleMask groupMask, final TupleMask columnMask) {
42 super(reteContainer, operator, groupMask, columnMask);
43 }
44
45 public FaithfulParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer,
46 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
47 final TupleMask groupMask, final int aggregatedColumn) {
48 this(reteContainer, operator, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth));
49 }
50
51 @Override
52 protected Map<AggregateResult, Diff<Timestamp>> doFoldingStep(final Tuple group, final FoldingState<Domain> state,
53 final Timestamp timestamp) {
54 final CumulativeAggregate<Domain, Accumulator> aggregate = getAggregate(group, timestamp);
55 if (state.delta.isEmpty()) {
56 gcAggregates(aggregate, group, timestamp);
57 return Collections.emptyMap();
58 } else {
59 final Map<AggregateResult, Diff<Timestamp>> diffMap = CollectionsFactory.createMap();
60 final Timestamp nextTimestamp = this.aggregates.get(group).higherKey(timestamp);
61
62 final AggregateResult currentOldResult = operator.getAggregate(aggregate.accumulator);
63
64 for (final Entry<Domain, Integer> entry : state.delta.entriesWithMultiplicities()) {
65 final boolean isInsertion = entry.getValue() > 0;
66 final Domain aggregand = entry.getKey();
67 for (int i = 0; i < Math.abs(entry.getValue()); i++) {
68 aggregate.accumulator = operator.update(aggregate.accumulator, aggregand, isInsertion);
69 }
70 }
71
72 final AggregateResult currentNewResult = operator.getAggregate(aggregate.accumulator);
73
74 if (!Objects.equals(currentOldResult, currentNewResult)) {
75 // current old result disappears here
76 appendDiff(currentOldResult, new Signed<>(Direction.DELETE, timestamp), diffMap);
77 if (nextTimestamp != null) {
78 appendDiff(currentOldResult, new Signed<>(Direction.INSERT, nextTimestamp), diffMap);
79 }
80
81 // current new result appears here
82 appendDiff(currentNewResult, new Signed<>(Direction.INSERT, timestamp), diffMap);
83 if (nextTimestamp != null) {
84 appendDiff(currentNewResult, new Signed<>(Direction.DELETE, nextTimestamp), diffMap);
85 }
86 }
87
88 gcAggregates(aggregate, group, timestamp);
89 updateTimeline(group, diffMap);
90
91 // prepare folding state for next timestamp
92 if (nextTimestamp != null) {
93 final FoldingState<Domain> newState = new FoldingState<>();
94 newState.delta = state.delta;
95 addFoldingState(group, newState, nextTimestamp);
96 }
97
98 return diffMap;
99 }
100 }
101
102 @Override
103 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
104 final Tuple group = groupMask.transform(update);
105 final Tuple value = columnMask.transform(update);
106 @SuppressWarnings("unchecked")
107 final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0));
108 final boolean isInsertion = direction == Direction.INSERT;
109
110 final CumulativeAggregate<Domain, Accumulator> aggregate = getAggregate(group, timestamp);
111 final FoldingState<Domain> state = new FoldingState<>();
112 if (isInsertion) {
113 aggregate.aggregands.addOne(aggregand);
114 state.delta.addOne(aggregand);
115 } else {
116 aggregate.aggregands.removeOne(aggregand);
117 state.delta.removeOne(aggregand);
118 }
119
120 addFoldingState(group, state, timestamp);
121 }
122
123 /**
124 * Garbage collects the counter of the given group and timestamp if the bag of aggregands is empty.
125 */
126 @Override
127 protected void gcAggregates(final CumulativeAggregate<Domain, Accumulator> aggregate, final Tuple group,
128 final Timestamp timestamp) {
129 if (aggregate.aggregands.isEmpty()) {
130 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator>> groupAggregates = this.aggregates
131 .get(group);
132 groupAggregates.remove(timestamp);
133 if (groupAggregates.isEmpty()) {
134 this.aggregates.remove(group);
135 }
136 }
137 }
138
139 /**
140 * On-demand initializes and returns the aggregate for the given group and timestamp.
141 */
142 @Override
143 protected CumulativeAggregate<Domain, Accumulator> getAggregate(final Tuple group, final Timestamp timestamp) {
144 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator>> groupAggregates = this.aggregates
145 .computeIfAbsent(group, k -> CollectionsFactory.createTreeMap());
146 return groupAggregates.computeIfAbsent(timestamp, k -> {
147 final CumulativeAggregate<Domain, Accumulator> aggregate = new CumulativeAggregate<>();
148 final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator>> lowerEntry = groupAggregates
149 .lowerEntry(timestamp);
150 if (lowerEntry == null) {
151 aggregate.accumulator = operator.createNeutral();
152 } else {
153 aggregate.accumulator = operator.clone(lowerEntry.getValue().accumulator);
154 }
155 return aggregate;
156 });
157 }
158
159 @Override
160 public AggregateResult getAggregateResult(final Tuple group) {
161 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator>> groupAggregates = this.aggregates.get(group);
162 if (groupAggregates != null) {
163 final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator>> lastEntry = groupAggregates.lastEntry();
164 return operator.getAggregate(lastEntry.getValue().accumulator);
165 } else {
166 return NEUTRAL;
167 }
168 }
169
170 protected static class CumulativeAggregate<Domain, Accumulator> {
171 protected Accumulator accumulator;
172 protected IDeltaBag<Domain> aggregands;
173
174 protected CumulativeAggregate() {
175 this.aggregands = CollectionsFactory.createDeltaBag();
176 }
177
178 @Override
179 public String toString() {
180 return "accumulator=" + accumulator + " aggregands=" + aggregands;
181 }
182 }
183
184 protected static class FoldingState<Domain> implements MergeableFoldingState<FoldingState<Domain>> {
185 protected IDeltaBag<Domain> delta;
186
187 protected FoldingState() {
188 this.delta = CollectionsFactory.createDeltaBag();
189 }
190
191 @Override
192 public String toString() {
193 return "delta=" + delta;
194 }
195
196 /**
197 * The returned result will never be null, even if the resulting delta set is empty.
198 */
199 @Override
200 public FoldingState<Domain> merge(final FoldingState<Domain> that) {
201 Preconditions.checkArgument(that != null);
202 // 'this' was the previously registered folding state
203 // 'that' is the new folding state being pushed upwards
204 final FoldingState<Domain> result = new FoldingState<>();
205 this.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m));
206 that.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m));
207 return result;
208 }
209
210 }
211
212}
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..cf2c2b2d
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java
@@ -0,0 +1,279 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation.timely;
10
11import java.util.Collections;
12import java.util.Map;
13import java.util.Map.Entry;
14import java.util.Objects;
15import java.util.TreeMap;
16
17import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Direction;
22import tools.refinery.viatra.runtime.matchers.util.IDeltaBag;
23import tools.refinery.viatra.runtime.matchers.util.Preconditions;
24import tools.refinery.viatra.runtime.matchers.util.Signed;
25import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
26import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode.CumulativeAggregate;
27import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode.FoldingState;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
30import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
31
32/**
33 * Faithful column aggregator with sequential aggregation architecture.
34 *
35 * @author Tamas Szabo
36 * @since 2.4
37 *
38 */
39public class FaithfulSequentialTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> extends
40 FaithfulTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult, CumulativeAggregate<Domain, Accumulator, AggregateResult>, FoldingState<Domain, AggregateResult>>
41 implements ResumableNode {
42
43 protected boolean isRecursiveAggregation;
44
45 public FaithfulSequentialTimelyColumnAggregatorNode(final ReteContainer reteContainer,
46 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
47 final TupleMask groupMask, final TupleMask columnMask) {
48 super(reteContainer, operator, groupMask, columnMask);
49 this.isRecursiveAggregation = false;
50 }
51
52 @Override
53 public void networkStructureChanged() {
54 super.networkStructureChanged();
55 this.isRecursiveAggregation = this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this);
56 }
57
58 @Override
59 protected Map<AggregateResult, Diff<Timestamp>> doFoldingStep(final Tuple group,
60 final FoldingState<Domain, AggregateResult> state, final Timestamp timestamp) {
61 final CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate = getAggregate(group, timestamp);
62 if (state.delta.isEmpty() && Objects.equals(state.oldResult, state.newResult)) {
63 gcAggregates(aggregate, group, timestamp);
64 return Collections.emptyMap();
65 } else {
66 final Map<AggregateResult, Diff<Timestamp>> diffMap = CollectionsFactory.createMap();
67 final Timestamp nextTimestamp = this.aggregates.get(group).higherKey(timestamp);
68
69 final AggregateResult previousOldResult = state.oldResult;
70 final AggregateResult previousNewResult = state.newResult;
71
72 final AggregateResult currentOldResult = previousOldResult == null
73 ? operator.getAggregate(aggregate.positive)
74 : operator.combine(previousOldResult, aggregate.positive);
75
76 for (final Entry<Domain, Integer> entry : state.delta.entriesWithMultiplicities()) {
77 final boolean isInsertion = entry.getValue() > 0;
78 final Domain aggregand = entry.getKey();
79 if (isInsertion) {
80 for (int i = 0; i < entry.getValue(); i++) {
81 if (isRecursiveAggregation) {
82 final boolean contains = aggregate.negative.containsNonZero(aggregand);
83 if (contains) {
84 aggregate.negative.addOne(aggregand);
85 } else {
86 aggregate.positive = operator.update(aggregate.positive, aggregand, true);
87 }
88 } else {
89 aggregate.positive = operator.update(aggregate.positive, aggregand, true);
90 }
91 }
92 } else {
93 for (int i = 0; i < -entry.getValue(); i++) {
94 if (isRecursiveAggregation) {
95 final boolean contains = operator.contains(aggregand, aggregate.positive);
96 if (contains) {
97 aggregate.positive = operator.update(aggregate.positive, aggregand, false);
98 } else {
99 aggregate.negative.removeOne(aggregand);
100 }
101 } else {
102 aggregate.positive = operator.update(aggregate.positive, aggregand, false);
103 }
104 }
105 }
106 }
107
108 final AggregateResult currentNewResult = previousNewResult == null
109 ? operator.getAggregate(aggregate.positive)
110 : operator.combine(previousNewResult, aggregate.positive);
111
112 aggregate.cachedResult = currentNewResult;
113
114 final boolean sameResult = Objects.equals(currentOldResult, currentNewResult);
115 if (!sameResult) {
116 // current old result disappears here
117 appendDiff(currentOldResult, new Signed<>(Direction.DELETE, timestamp), diffMap);
118 if (nextTimestamp != null) {
119 appendDiff(currentOldResult, new Signed<>(Direction.INSERT, nextTimestamp), diffMap);
120 }
121
122 // current new result appears here
123 appendDiff(currentNewResult, new Signed<>(Direction.INSERT, timestamp), diffMap);
124 if (nextTimestamp != null) {
125 appendDiff(currentNewResult, new Signed<>(Direction.DELETE, nextTimestamp), diffMap);
126 }
127 }
128
129 gcAggregates(aggregate, group, timestamp);
130 updateTimeline(group, diffMap);
131
132 // prepare folding state for next timestamp
133 if (nextTimestamp != null && !sameResult) {
134 final FoldingState<Domain, AggregateResult> newState = new FoldingState<>();
135 // DO NOT push forward the delta in the folding state!!! that one only affects the input timestamp
136 newState.oldResult = currentOldResult;
137 newState.newResult = currentNewResult;
138 addFoldingState(group, newState, nextTimestamp);
139 }
140
141 return diffMap;
142 }
143 }
144
145 @Override
146 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
147 final Tuple group = groupMask.transform(update);
148 final Tuple value = columnMask.transform(update);
149 @SuppressWarnings("unchecked")
150 final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0));
151 final boolean isInsertion = direction == Direction.INSERT;
152
153 final AggregateResult previousResult = getResultRaw(group, timestamp, true);
154 final FoldingState<Domain, AggregateResult> state = new FoldingState<Domain, AggregateResult>();
155 if (isInsertion) {
156 state.delta.addOne(aggregand);
157 } else {
158 state.delta.removeOne(aggregand);
159 }
160 state.oldResult = previousResult;
161 state.newResult = previousResult;
162
163 // it is acceptable if both oldResult and newResult are null at this point
164 // in that case we did not have a previous entry at a lower timestamp
165
166 addFoldingState(group, state, timestamp);
167 }
168
169 protected AggregateResult getResultRaw(final Tuple group, final Timestamp timestamp, final boolean lower) {
170 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> entryMap = this.aggregates
171 .get(group);
172 if (entryMap == null) {
173 return null;
174 } else {
175 CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate = null;
176 if (lower) {
177 final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> lowerEntry = entryMap
178 .lowerEntry(timestamp);
179 if (lowerEntry != null) {
180 aggregate = lowerEntry.getValue();
181 }
182 } else {
183 aggregate = entryMap.get(timestamp);
184 }
185 if (aggregate == null) {
186 return null;
187 } else {
188 return aggregate.cachedResult;
189 }
190 }
191 }
192
193 @Override
194 protected void gcAggregates(final CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate,
195 final Tuple group, final Timestamp timestamp) {
196 if (operator.isNeutral(aggregate.positive) && aggregate.negative.isEmpty()) {
197 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> groupAggregates = this.aggregates
198 .get(group);
199 groupAggregates.remove(timestamp);
200 if (groupAggregates.isEmpty()) {
201 this.aggregates.remove(group);
202 }
203 }
204 }
205
206 @Override
207 protected CumulativeAggregate<Domain, Accumulator, AggregateResult> getAggregate(final Tuple group,
208 final Timestamp timestamp) {
209 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> groupAggregates = this.aggregates
210 .computeIfAbsent(group, k -> CollectionsFactory.createTreeMap());
211 return groupAggregates.computeIfAbsent(timestamp, k -> {
212 final CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate = new CumulativeAggregate<>();
213 aggregate.positive = operator.createNeutral();
214 return aggregate;
215 });
216 }
217
218 @Override
219 public AggregateResult getAggregateResult(final Tuple group) {
220 final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> groupAggregates = this.aggregates
221 .get(group);
222 if (groupAggregates != null) {
223 final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> lastEntry = groupAggregates
224 .lastEntry();
225 return lastEntry.getValue().cachedResult;
226 } else {
227 return NEUTRAL;
228 }
229 }
230
231 protected static class CumulativeAggregate<Domain, Accumulator, AggregateResult> {
232 protected Accumulator positive;
233 protected IDeltaBag<Domain> negative;
234 protected AggregateResult cachedResult;
235
236 protected CumulativeAggregate() {
237 this.negative = CollectionsFactory.createDeltaBag();
238 }
239
240 @Override
241 public String toString() {
242 return "positive=" + positive + " negative=" + negative + " cachedResult=" + cachedResult;
243 }
244 }
245
246 protected static class FoldingState<Domain, AggregateResult>
247 implements MergeableFoldingState<FoldingState<Domain, AggregateResult>> {
248 protected IDeltaBag<Domain> delta;
249 protected AggregateResult oldResult;
250 protected AggregateResult newResult;
251
252 protected FoldingState() {
253 this.delta = CollectionsFactory.createDeltaBag();
254 }
255
256 @Override
257 public String toString() {
258 return "delta=" + delta + " oldResult=" + oldResult + " newResult=" + newResult;
259 }
260
261 /**
262 * The returned result will never be null, even if the resulting delta set is empty.
263 */
264 @Override
265 public FoldingState<Domain, AggregateResult> merge(final FoldingState<Domain, AggregateResult> that) {
266 Preconditions.checkArgument(that != null);
267 // 'this' was the previously registered folding state
268 // 'that' is the new folding state being pushed upwards
269 final FoldingState<Domain, AggregateResult> result = new FoldingState<Domain, AggregateResult>();
270 this.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m));
271 that.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m));
272 result.oldResult = this.oldResult;
273 result.newResult = that.newResult;
274 return result;
275 }
276
277 }
278
279}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation.timely;
10
11import java.util.Collections;
12import java.util.Map;
13import java.util.Map.Entry;
14import java.util.Objects;
15import java.util.TreeMap;
16
17import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Signed;
22import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines;
25import tools.refinery.viatra.runtime.rete.aggregation.AbstractColumnAggregatorNode;
26import tools.refinery.viatra.runtime.rete.aggregation.GroupedMap;
27import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulTimelyColumnAggregatorNode.MergeableFoldingState;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
30import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
31import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
32import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
33import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
34
35/**
36 * Faithful timely implementation of the column aggregator node. Complete timelines (series of appearance &
37 * disappearance) are maintained for tuples. <br>
38 * <br>
39 * Subclasses are responsible for implementing the aggregator architecture, and they must use the CumulativeAggregate
40 * type parameter for that. <br>
41 * <br>
42 * This node supports recursive aggregation.
43 *
44 * @author Tamas Szabo
45 * @since 2.4
46 */
47public abstract class FaithfulTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult, CumulativeAggregate, FoldingState extends MergeableFoldingState<FoldingState>>
48 extends AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult> implements ResumableNode {
49
50 protected final Map<Tuple, TreeMap<Timestamp, CumulativeAggregate>> aggregates;
51 protected final Map<Tuple, Map<AggregateResult, Timeline<Timestamp>>> timelines;
52 protected final TreeMap<Timestamp, Map<Tuple, FoldingState>> foldingState;
53 protected CommunicationGroup communicationGroup;
54
55 public FaithfulTimelyColumnAggregatorNode(final ReteContainer reteContainer,
56 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
57 final TupleMask groupMask, final TupleMask columnMask) {
58 super(reteContainer, operator, groupMask, columnMask);
59 this.aggregates = CollectionsFactory.createMap();
60 this.timelines = CollectionsFactory.createMap();
61 this.foldingState = CollectionsFactory.createTreeMap();
62 // mailbox MUST be instantiated after the fields are all set
63 this.mailbox = instantiateMailbox();
64 }
65
66 @Override
67 protected Mailbox instantiateMailbox() {
68 return new TimelyMailbox(this, this.reteContainer);
69 }
70
71 @Override
72 public void clear() {
73 this.mailbox.clear();
74 this.aggregates.clear();
75 this.timelines.clear();
76 this.children.clear();
77 this.childMailboxes.clear();
78 this.foldingState.clear();
79 }
80
81 /**
82 * Registers the given folding state for the specified timestamp and tuple. If there is already a state stored, the
83 * two states will be merged together.
84 *
85 *
86 */
87 protected void addFoldingState(final Tuple group, final FoldingState state, final Timestamp timestamp) {
88 // assert !state.delta.isEmpty();
89 final Map<Tuple, FoldingState> tupleMap = this.foldingState.computeIfAbsent(timestamp,
90 k -> CollectionsFactory.createMap());
91 tupleMap.compute(group, (k, v) -> {
92 return v == null ? state : v.merge(state);
93 });
94 }
95
96 @Override
97 public Timestamp getResumableTimestamp() {
98 if (this.foldingState.isEmpty()) {
99 return null;
100 } else {
101 return this.foldingState.firstKey();
102 }
103 }
104
105 @Override
106 public void resumeAt(final Timestamp timestamp) {
107 Timestamp current = this.getResumableTimestamp();
108 if (current == null) {
109 throw new IllegalStateException("There is nothing to fold!");
110 } else if (current.compareTo(timestamp) != 0) {
111 throw new IllegalStateException("Expected to continue folding at " + timestamp + "!");
112 }
113
114 final Map<Tuple, FoldingState> tupleMap = this.foldingState.remove(timestamp);
115 for (final Entry<Tuple, FoldingState> groupEntry : tupleMap.entrySet()) {
116 final Tuple group = groupEntry.getKey();
117 final FoldingState value = groupEntry.getValue();
118 final Map<AggregateResult, Diff<Timestamp>> diffMap = doFoldingStep(group, value, timestamp);
119 for (final Entry<AggregateResult, Diff<Timestamp>> resultEntry : diffMap.entrySet()) {
120 for (final Signed<Timestamp> signed : resultEntry.getValue()) {
121 propagate(signed.getDirection(), group, resultEntry.getKey(), signed.getPayload());
122 }
123 }
124 }
125
126 final Timestamp nextTimestamp = this.getResumableTimestamp();
127 if (Objects.equals(timestamp, nextTimestamp)) {
128 throw new IllegalStateException(
129 "Folding at " + timestamp + " produced more folding work at the same timestamp!");
130 } else if (nextTimestamp != null) {
131 this.communicationGroup.notifyHasMessage(this.mailbox, nextTimestamp);
132 }
133 }
134
135 protected abstract Map<AggregateResult, Diff<Timestamp>> doFoldingStep(final Tuple group, final FoldingState state,
136 final Timestamp timestamp);
137
138 /**
139 * Updates and garbage collects the timeline of the given tuple based on the given diffs.
140 */
141 protected void updateTimeline(final Tuple group, final Map<AggregateResult, Diff<Timestamp>> diffs) {
142 if (!diffs.isEmpty()) {
143 this.timelines.compute(group, (k, resultTimelines) -> {
144 if (resultTimelines == null) {
145 resultTimelines = CollectionsFactory.createMap();
146 }
147 for (final Entry<AggregateResult, Diff<Timestamp>> entry : diffs.entrySet()) {
148 final AggregateResult result = entry.getKey();
149 resultTimelines.compute(result, (k2, oldResultTimeline) -> {
150 final Diff<Timestamp> currentResultDiffs = entry.getValue();
151 if (oldResultTimeline == null) {
152 oldResultTimeline = getInitialTimeline(result);
153 }
154 final Timeline<Timestamp> timeline = oldResultTimeline.mergeAdditive(currentResultDiffs);
155 if (timeline.isEmpty()) {
156 return null;
157 } else {
158 return timeline;
159 }
160 });
161 }
162 if (resultTimelines.isEmpty()) {
163 return null;
164 } else {
165 return resultTimelines;
166 }
167 });
168 }
169 }
170
171 /**
172 * Garbage collects the counter of the given group and timestamp if the bag of aggregands is empty.
173 */
174 protected abstract void gcAggregates(final CumulativeAggregate aggregate, final Tuple group,
175 final Timestamp timestamp);
176
177 /**
178 * On-demand initializes and returns the aggregate for the given group and timestamp.
179 */
180 protected abstract CumulativeAggregate getAggregate(final Tuple group, final Timestamp timestamp);
181
182 protected static final Timeline<Timestamp> NEUTRAL_INITIAL_TIMELINE = Timestamp.INSERT_AT_ZERO_TIMELINE;
183 protected static final Timeline<Timestamp> NON_NEUTRAL_INITIAL_TIMELINE = Timelines.createEmpty();
184
185 protected Timeline<Timestamp> getInitialTimeline(final AggregateResult result) {
186 if (NEUTRAL == result) {
187 return NEUTRAL_INITIAL_TIMELINE;
188 } else {
189 return NON_NEUTRAL_INITIAL_TIMELINE;
190 }
191 }
192
193 protected static <AggregateResult> void appendDiff(final AggregateResult result, final Signed<Timestamp> diff,
194 final Map<AggregateResult, Diff<Timestamp>> diffs) {
195 if (result != null) {
196 diffs.compute(result, (k, timeLineDiff) -> {
197 if (timeLineDiff == null) {
198 timeLineDiff = new Diff<>();
199 }
200 timeLineDiff.add(diff);
201 return timeLineDiff;
202 });
203 }
204 }
205
206 @Override
207 public Tuple getAggregateTuple(final Tuple group) {
208 return tupleFromAggregateResult(group, getAggregateResult(group));
209 }
210
211 @Override
212 public Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(final Tuple group) {
213 final Map<AggregateResult, Timeline<Timestamp>> resultTimelines = this.timelines.get(group);
214 if (resultTimelines == null) {
215 if (NEUTRAL == null) {
216 return Collections.emptyMap();
217 } else {
218 return Collections.singletonMap(NEUTRAL, NEUTRAL_INITIAL_TIMELINE);
219 }
220 } else {
221 return resultTimelines;
222 }
223 }
224
225 @Override
226 public Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(final Tuple group) {
227 final Map<AggregateResult, Timeline<Timestamp>> resultTimelines = getAggregateResultTimeline(group);
228 return new GroupedMap<AggregateResult, Timeline<Timestamp>>(group, resultTimelines, this.runtimeContext);
229 }
230
231 @Override
232 public CommunicationGroup getCurrentGroup() {
233 return communicationGroup;
234 }
235
236 @Override
237 public void setCurrentGroup(final CommunicationGroup currentGroup) {
238 this.communicationGroup = currentGroup;
239 }
240
241 protected interface MergeableFoldingState<T> {
242
243 public abstract T merge(final T that);
244
245 }
246
247} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation.timely;
10
11import java.util.Map.Entry;
12import java.util.TreeMap;
13
14import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20
21/**
22 * First-only column aggregator with parallel aggregation architecture.
23 *
24 * @author Tamas Szabo
25 * @since 2.4
26 */
27public class FirstOnlyParallelTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult>
28 extends FirstOnlyTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> {
29
30 public FirstOnlyParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer,
31 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
32 final TupleMask groupMask, final TupleMask columnMask) {
33 super(reteContainer, operator, groupMask, columnMask);
34 }
35
36 /**
37 * Accumulator gets modified at the input timestamp and at all higher timestamps. Folding cannot be interrupted if
38 * the new aggregate result is the same as the old at an intermediate timestamp because aggregands need to be copied
39 * over to all accumulators at the higher timestamps.
40 */
41 @Override
42 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
43 final Tuple group = groupMask.transform(update);
44 final Tuple value = columnMask.transform(update);
45 @SuppressWarnings("unchecked")
46 final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0));
47 final boolean isInsertion = direction == Direction.INSERT;
48
49 final AggregateResult previousResult = getResultRaw(group, timestamp, true);
50
51 Accumulator oldAccumulator = getAccumulator(group, timestamp);
52 AggregateResult oldResult = operator.getAggregate(oldAccumulator);
53
54 Accumulator newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion);
55 AggregateResult newResult = operator.getAggregate(newAccumulator);
56
57 storeIfNotNeutral(group, newAccumulator, newResult, timestamp);
58
59 propagateWithChecks(group, timestamp, previousResult, previousResult, oldResult, newResult);
60
61 AggregateResult previousOldResult = oldResult;
62 AggregateResult previousNewResult = newResult;
63 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupEntries = this.memory
64 .get(group);
65
66 Timestamp currentTimestamp = groupEntries == null ? null : groupEntries.higherKey(timestamp);
67
68 while (currentTimestamp != null) {
69 final CumulativeAggregate<Accumulator, AggregateResult> groupEntry = groupEntries.get(currentTimestamp);
70 oldResult = groupEntry.result;
71 oldAccumulator = groupEntry.accumulator;
72 newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion);
73 newResult = operator.getAggregate(newAccumulator);
74
75 storeIfNotNeutral(group, newAccumulator, newResult, currentTimestamp);
76
77 propagateWithChecks(group, currentTimestamp, previousOldResult, previousNewResult, oldResult, newResult);
78
79 previousOldResult = oldResult;
80 previousNewResult = newResult;
81 currentTimestamp = groupEntries.higherKey(currentTimestamp);
82 }
83 }
84
85 @Override
86 protected Accumulator getAccumulator(final Tuple group, final Timestamp timestamp) {
87 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group);
88 if (entryMap == null) {
89 return operator.createNeutral();
90 } else {
91 final CumulativeAggregate<Accumulator, AggregateResult> entry = entryMap.get(timestamp);
92 if (entry == null) {
93 final Entry<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> lowerEntry = entryMap
94 .lowerEntry(timestamp);
95 if (lowerEntry == null) {
96 return operator.createNeutral();
97 } else {
98 return operator.clone(lowerEntry.getValue().accumulator);
99 }
100 } else {
101 return entry.accumulator;
102 }
103 }
104 }
105
106}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation.timely;
10
11import java.util.Objects;
12import java.util.TreeMap;
13
14import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20
21/**
22 * First-only column aggregator with sequential aggregation architecture.
23 *
24 * @author Tamas Szabo
25 * @since 2.4
26 */
27public class FirstOnlySequentialTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult>
28 extends FirstOnlyTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> {
29
30 public FirstOnlySequentialTimelyColumnAggregatorNode(final ReteContainer reteContainer,
31 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
32 final TupleMask groupMask, final TupleMask columnMask) {
33 super(reteContainer, operator, groupMask, columnMask);
34 }
35
36 /**
37 * Accumulator gets modified only at the timestamp where the update happened. During the folding, accumulators are
38 * never changed at higher timestamps. Aggregate results at higher timestamps may change due to the change at the
39 * input timestamp. Uniqueness enforcement may require from aggregate results to jump up/down on demand during the
40 * folding.
41 */
42 @Override
43 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
44 final Tuple group = groupMask.transform(update);
45 final Tuple value = columnMask.transform(update);
46 @SuppressWarnings("unchecked")
47 final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0));
48 final boolean isInsertion = direction == Direction.INSERT;
49
50 final AggregateResult previousResult = getResultRaw(group, timestamp, true);
51
52 final Accumulator oldAccumulator = getAccumulator(group, timestamp);
53 final AggregateResult oldResult = previousResult == null ? operator.getAggregate(oldAccumulator)
54 : operator.combine(previousResult, oldAccumulator);
55
56 final Accumulator newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion);
57 final AggregateResult newResult = previousResult == null ? operator.getAggregate(newAccumulator)
58 : operator.combine(previousResult, newAccumulator);
59
60 storeIfNotNeutral(group, newAccumulator, newResult, timestamp);
61
62 propagateWithChecks(group, timestamp, previousResult, previousResult, oldResult, newResult);
63
64 // fold up the state towards higher timestamps
65 if (!Objects.equals(oldResult, newResult)) {
66 AggregateResult previousOldResult = oldResult;
67 AggregateResult previousNewResult = newResult;
68 AggregateResult currentOldResult = null;
69 AggregateResult currentNewResult = null;
70 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupEntries = this.memory
71 .get(group);
72
73 Timestamp currentTimestamp = groupEntries == null ? null : groupEntries.higherKey(timestamp);
74
75 while (currentTimestamp != null) {
76 // they cannot be the same, otherwise we would not even be here
77 assert !Objects.equals(previousOldResult, previousNewResult);
78
79 final Accumulator accumulator = getAccumulator(group, currentTimestamp);
80 currentOldResult = groupEntries.get(currentTimestamp).result;
81 currentNewResult = operator.combine(previousNewResult, accumulator);
82
83 // otherwise we would not be iterating over this timestamp
84 assert !operator.isNeutral(accumulator);
85
86 propagateWithChecks(group, currentTimestamp, previousOldResult, previousNewResult, currentOldResult,
87 currentNewResult);
88
89 if (!Objects.equals(currentOldResult, currentNewResult)) {
90 storeIfNotNeutral(group, accumulator, currentNewResult, currentTimestamp);
91 previousOldResult = currentOldResult;
92 previousNewResult = currentNewResult;
93 currentTimestamp = groupEntries.higherKey(currentTimestamp);
94 } else {
95 // we can stop the folding from here
96 break;
97 }
98 }
99 }
100 }
101
102 @Override
103 protected Accumulator getAccumulator(final Tuple group, final Timestamp timestamp) {
104 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group);
105 if (entryMap == null) {
106 return operator.createNeutral();
107 } else {
108 final CumulativeAggregate<Accumulator, AggregateResult> entry = entryMap.get(timestamp);
109 if (entry == null) {
110 return operator.createNeutral();
111 } else {
112 return entry.accumulator;
113 }
114 }
115 }
116
117}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.aggregation.timely;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Objects;
16import java.util.TreeMap;
17
18import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines;
25import tools.refinery.viatra.runtime.rete.aggregation.AbstractColumnAggregatorNode;
26import tools.refinery.viatra.runtime.rete.aggregation.GroupedMap;
27import tools.refinery.viatra.runtime.rete.network.ReteContainer;
28import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
29import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
30import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
31
32/**
33 * First-only timely implementation of the column aggregator node. Only timestamps of appearance are maintained for
34 * tuples instead of complete timelines.
35 * <br><br>
36 * Subclasses are responsible for implementing the aggregator architecture, and they must make use of the inner class {@link CumulativeAggregate}.
37 * <br><br>
38 * This node supports recursive aggregation.
39 *
40 * @author Tamas Szabo
41 * @since 2.4
42 */
43public abstract class FirstOnlyTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult>
44 extends AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult> {
45
46 protected final Map<Tuple, TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>>> memory;
47
48 public FirstOnlyTimelyColumnAggregatorNode(final ReteContainer reteContainer,
49 final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator,
50 final TupleMask groupMask, final TupleMask columnMask) {
51 super(reteContainer, operator, groupMask, columnMask);
52 this.memory = CollectionsFactory.createMap();
53 // mailbox MUST be instantiated after the fields are all set
54 this.mailbox = instantiateMailbox();
55 }
56
57 protected static class CumulativeAggregate<Accumulator, AggregateResult> {
58 // the accumulator storing the aggregands
59 protected Accumulator accumulator;
60 // the aggregate result at the timestamp where this cumulative aggregate is stored
61 protected AggregateResult result;
62
63 private CumulativeAggregate(final Accumulator accumulator, final AggregateResult result) {
64 this.accumulator = accumulator;
65 this.result = result;
66 }
67
68 }
69
70 public Collection<Tuple> getGroups() {
71 return this.memory.keySet();
72 }
73
74 public AggregateResult getLastResult(final Tuple group) {
75 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupMap = this.memory.get(group);
76 if (groupMap == null) {
77 return null;
78 } else {
79 return groupMap.lastEntry().getValue().result;
80 }
81 }
82
83 public Timestamp getLastTimestamp(final Tuple group) {
84 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupMap = this.memory.get(group);
85 if (groupMap == null) {
86 return null;
87 } else {
88 return groupMap.lastEntry().getKey();
89 }
90 }
91
92 @Override
93 protected Mailbox instantiateMailbox() {
94 return new TimelyMailbox(this, this.reteContainer);
95 }
96
97 @Override
98 public void clear() {
99 this.mailbox.clear();
100 this.memory.clear();
101 this.children.clear();
102 this.childMailboxes.clear();
103 }
104
105 protected void propagateWithChecks(final Tuple group, final Timestamp timestamp,
106 final AggregateResult previousOldResult, final AggregateResult previousNewResult,
107 final AggregateResult currentOldResult, final AggregateResult currentNewResult) {
108 final boolean jumpDown = Objects.equals(previousNewResult, currentOldResult);
109 final boolean jumpUp = Objects.equals(previousOldResult, currentNewResult);
110 final boolean resultsDiffer = !Objects.equals(currentOldResult, currentNewResult);
111
112 // uniqueness enforcement is happening here
113 if ((resultsDiffer || jumpDown) && !Objects.equals(previousOldResult, currentOldResult)) {
114 propagate(Direction.DELETE, group, currentOldResult, timestamp);
115 }
116 if ((resultsDiffer || jumpUp) && !Objects.equals(previousNewResult, currentNewResult)) {
117 propagate(Direction.INSERT, group, currentNewResult, timestamp);
118 }
119 }
120
121 /**
122 * Returns the aggregation architecture-specific accumulator at the specified timestamp for the given group.
123 */
124 protected abstract Accumulator getAccumulator(final Tuple group, final Timestamp timestamp);
125
126 protected AggregateResult getResultRaw(final Tuple group, final Timestamp timestamp, final boolean lower) {
127 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group);
128 if (entryMap == null) {
129 return null;
130 } else {
131 CumulativeAggregate<Accumulator, AggregateResult> entry = null;
132 if (lower) {
133 final Entry<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> lowerEntry = entryMap
134 .lowerEntry(timestamp);
135 if (lowerEntry != null) {
136 entry = lowerEntry.getValue();
137 }
138 } else {
139 entry = entryMap.get(timestamp);
140 }
141 if (entry == null) {
142 return null;
143 } else {
144 return entry.result;
145 }
146 }
147 }
148
149 protected AggregateResult getResult(final Tuple group, final Timestamp timestamp, final boolean lower) {
150 final AggregateResult result = getResultRaw(group, timestamp, lower);
151 if (result == null) {
152 return NEUTRAL;
153 } else {
154 return result;
155 }
156 }
157
158 protected AggregateResult getResult(final Tuple group, final Timestamp timestamp) {
159 return getResult(group, timestamp, false);
160 }
161
162 protected void storeIfNotNeutral(final Tuple group, final Accumulator accumulator, final AggregateResult value,
163 final Timestamp timestamp) {
164 TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group);
165 if (operator.isNeutral(accumulator)) {
166 if (entryMap != null) {
167 entryMap.remove(timestamp);
168 if (entryMap.isEmpty()) {
169 this.memory.remove(group);
170 }
171 }
172 } else {
173 if (entryMap == null) {
174 entryMap = CollectionsFactory.createTreeMap();
175 this.memory.put(group, entryMap);
176 }
177 entryMap.put(timestamp, new CumulativeAggregate<>(accumulator, value));
178 }
179 }
180
181 @Override
182 public Tuple getAggregateTuple(final Tuple group) {
183 return tupleFromAggregateResult(group, getResult(group, Timestamp.ZERO));
184 }
185
186 @Override
187 public AggregateResult getAggregateResult(final Tuple group) {
188 return getResult(group, Timestamp.ZERO);
189 }
190
191 @Override
192 public Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(final Tuple group) {
193 final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group);
194 if (entryMap == null) {
195 return Collections.emptyMap();
196 } else {
197 final Map<AggregateResult, Timeline<Timestamp>> result = CollectionsFactory.createMap();
198 for (final Entry<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entry : entryMap
199 .descendingMap().entrySet()) {
200 result.put(entry.getValue().result, Timelines.createFrom(entry.getKey()));
201 }
202 return result;
203 }
204 }
205
206 @Override
207 public Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(final Tuple group) {
208 final Map<AggregateResult, Timeline<Timestamp>> resultTimelines = getAggregateResultTimeline(group);
209 return new GroupedMap<AggregateResult, Timeline<Timestamp>>(group, resultTimelines, this.runtimeContext);
210 }
211
212}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.boundary;
11
12/**
13 * For objects that connect a RETE implementation to the underlying model.
14 *
15 * @author Gabor Bergmann
16 *
17 */
18public interface Disconnectable {
19
20 /**
21 * Disconnects this rete engine component from the underlying model. Disconnecting enables the garbage collection
22 * mechanisms to dispose of the rete network.
23 */
24 void disconnect();
25
26}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.boundary;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.context.IInputKey;
16import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24import tools.refinery.viatra.runtime.rete.matcher.ReteEngine;
25import tools.refinery.viatra.runtime.rete.network.Network;
26import tools.refinery.viatra.runtime.rete.network.Receiver;
27import tools.refinery.viatra.runtime.rete.network.ReteContainer;
28import tools.refinery.viatra.runtime.rete.network.StandardNode;
29import tools.refinery.viatra.runtime.rete.network.Supplier;
30import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
31import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
32import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
33import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
34import tools.refinery.viatra.runtime.rete.remote.Address;
35
36/**
37 * An input node representing an enumerable extensional input relation and receiving external updates.
38 *
39 * <p>
40 * Contains those tuples that are in the extensional relation identified by the input key, and also conform to the
41 * global seed (if any).
42 *
43 * @author Bergmann Gabor
44 *
45 */
46public class ExternalInputEnumeratorNode extends StandardNode
47 implements Disconnectable, Receiver, IQueryRuntimeContextListener {
48
49 private IQueryRuntimeContext context = null;
50 private IInputKey inputKey;
51 private Tuple globalSeed;
52 private InputConnector inputConnector;
53 private Network network;
54 private Address<? extends Receiver> myAddress;
55 private boolean parallelExecutionEnabled;
56 /**
57 * @since 1.6
58 */
59 protected final Mailbox mailbox;
60 private final IQueryBackendContext qBackendContext;
61
62 public ExternalInputEnumeratorNode(ReteContainer reteContainer) {
63 super(reteContainer);
64 myAddress = Address.of(this);
65 network = reteContainer.getNetwork();
66 inputConnector = network.getInputConnector();
67 qBackendContext = network.getEngine().getBackendContext();
68 mailbox = instantiateMailbox();
69 reteContainer.registerClearable(mailbox);
70 }
71
72 /**
73 * Instantiates the {@link Mailbox} of this receiver. Subclasses may override this method to provide their own
74 * mailbox implementation.
75 *
76 * @return the mailbox
77 * @since 2.0
78 */
79 protected Mailbox instantiateMailbox() {
80 if (this.reteContainer.isTimelyEvaluation()) {
81 return new TimelyMailbox(this, this.reteContainer);
82 } else {
83 return new BehaviorChangingMailbox(this, this.reteContainer);
84 }
85 }
86
87 @Override
88 public Mailbox getMailbox() {
89 return this.mailbox;
90 }
91
92 public void connectThroughContext(ReteEngine engine, IInputKey inputKey, Tuple globalSeed) {
93 this.inputKey = inputKey;
94 this.globalSeed = globalSeed;
95 setTag(inputKey);
96
97 final IQueryRuntimeContext context = engine.getRuntimeContext();
98 if (!context.getMetaContext().isEnumerable(inputKey))
99 throw new IllegalArgumentException(this.getClass().getSimpleName()
100 + " only applicable for enumerable input keys; received instead " + inputKey);
101
102 this.context = context;
103 this.parallelExecutionEnabled = engine.isParallelExecutionEnabled();
104
105 engine.addDisconnectable(this);
106 context.addUpdateListener(inputKey, globalSeed, this);
107 }
108
109 @Override
110 public void disconnect() {
111 if (context != null) { // if connected
112 context.removeUpdateListener(inputKey, globalSeed, this);
113 context = null;
114 }
115 }
116
117 /**
118 * @since 2.2
119 */
120 protected Iterable<Tuple> getTuplesInternal() {
121 Iterable<Tuple> tuples = null;
122
123 if (context != null) { // if connected
124 if (globalSeed == null) {
125 tuples = context.enumerateTuples(inputKey, TupleMask.empty(inputKey.getArity()),
126 Tuples.staticArityFlatTupleOf());
127 } else {
128 final TupleMask mask = TupleMask.fromNonNullIndices(globalSeed);
129 tuples = context.enumerateTuples(inputKey, mask, mask.transform(globalSeed));
130 }
131 }
132
133 return tuples;
134 }
135
136 @Override
137 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
138 final Iterable<Tuple> tuples = getTuplesInternal();
139 if (tuples != null) {
140 for (final Tuple tuple : tuples) {
141 collector.add(tuple);
142 }
143 }
144 }
145
146 @Override
147 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
148 final Iterable<Tuple> tuples = getTuplesInternal();
149 if (tuples != null) {
150 for (final Tuple tuple : tuples) {
151 collector.put(tuple, Timestamp.INSERT_AT_ZERO_TIMELINE);
152 }
153 }
154 }
155
156 /* Update from runtime context */
157 @Override
158 public void update(IInputKey key, Tuple update, boolean isInsertion) {
159 if (parallelExecutionEnabled) {
160 // send back to myself as an official external update, and then propagate it transparently
161 network.sendExternalUpdate(myAddress, direction(isInsertion), update);
162 } else {
163 if (qBackendContext.areUpdatesDelayed()) {
164 // post the update into the mailbox of the node
165 mailbox.postMessage(direction(isInsertion), update, Timestamp.ZERO);
166 } else {
167 // just propagate the input
168 update(direction(isInsertion), update, Timestamp.ZERO);
169 }
170 // if the the update method is called from within a delayed execution,
171 // the following invocation will be a no-op
172 network.waitForReteTermination();
173 }
174 }
175
176 private static Direction direction(boolean isInsertion) {
177 return isInsertion ? Direction.INSERT : Direction.DELETE;
178 }
179
180 /* Self-addressed from network */
181 @Override
182 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
183 propagateUpdate(direction, updateElement, timestamp);
184 }
185
186 @Override
187 public void appendParent(Supplier supplier) {
188 throw new UnsupportedOperationException("Input nodes can't have parents");
189 }
190
191 @Override
192 public void removeParent(Supplier supplier) {
193 throw new UnsupportedOperationException("Input nodes can't have parents");
194 }
195
196 @Override
197 public Collection<Supplier> getParents() {
198 return Collections.emptySet();
199 }
200
201 public IInputKey getInputKey() {
202 return inputKey;
203 }
204
205 public Tuple getGlobalSeed() {
206 return globalSeed;
207 }
208
209}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.boundary;
10
11import tools.refinery.viatra.runtime.matchers.context.IInputKey;
12import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
15import tools.refinery.viatra.runtime.rete.matcher.ReteEngine;
16import tools.refinery.viatra.runtime.rete.network.ReteContainer;
17import tools.refinery.viatra.runtime.rete.single.FilterNode;
18
19/**
20 * A filter node representing a (stateless, typically non-enumerable) extensional input relation.
21 *
22 * <p> 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.
23 *
24 * @author Bergmann Gabor
25 *
26 */
27public class ExternalInputStatelessFilterNode extends FilterNode implements Disconnectable {
28
29 IQueryRuntimeContext context = null;
30 IInputKey inputKey;
31 private InputConnector inputConnector;
32 private TupleMask mask;
33
34 public ExternalInputStatelessFilterNode(ReteContainer reteContainer, TupleMask mask) {
35 super(reteContainer);
36 this.mask = mask;
37 this.inputConnector = reteContainer.getNetwork().getInputConnector();
38 }
39
40 @Override
41 public boolean check(Tuple ps) {
42 if (mask != null)
43 ps = mask.transform(ps);
44 return context.containsTuple(inputKey, ps);
45 }
46
47
48 public void connectThroughContext(ReteEngine engine, IInputKey inputKey) {
49 this.inputKey = inputKey;
50 setTag(inputKey);
51
52 final IQueryRuntimeContext context = engine.getRuntimeContext();
53 if (!context.getMetaContext().isStateless(inputKey))
54 throw new IllegalArgumentException(
55 this.getClass().getSimpleName() +
56 " only applicable for stateless input keys; received instead " +
57 inputKey);
58
59 this.context = context;
60
61 engine.addDisconnectable(this);
62 }
63
64 @Override
65 public void disconnect() {
66 this.context = null;
67 }
68}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.boundary;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14import java.util.stream.Stream;
15
16import tools.refinery.viatra.runtime.matchers.context.IInputKey;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.rete.network.Network;
21import tools.refinery.viatra.runtime.rete.network.Node;
22import tools.refinery.viatra.runtime.rete.recipes.InputFilterRecipe;
23import tools.refinery.viatra.runtime.rete.recipes.InputRecipe;
24import tools.refinery.viatra.runtime.rete.remote.Address;
25
26/**
27 * A class responsible for connecting input nodes to the runtime context.
28 *
29 * @author Bergmann Gabor
30 *
31 */
32public final class InputConnector {
33 Network network;
34
35 private Map<IInputKey, Map<Tuple, Address<ExternalInputEnumeratorNode>>> externalInputRoots = CollectionsFactory.createMap();
36
37// /*
38// * arity:1 used as simple entity constraints label is the object representing the type null label means all entities
39// * regardless of type (global supertype), if allowed
40// */
41// protected Map<Object, Address<? extends Tunnel>> unaryRoots = CollectionsFactory.getMap();
42// /*
43// * arity:3 (rel, from, to) used as VPM relation constraints null label means all relations regardless of type
44// * (global supertype)
45// */
46// protected Map<Object, Address<? extends Tunnel>> ternaryEdgeRoots = CollectionsFactory.getMap();
47// /*
48// * arity:2 (from, to) not used over VPM; can be used as EMF references for instance label is the object representing
49// * the type null label means all entities regardless of type if allowed (global supertype), if allowed
50// */
51// protected Map<Object, Address<? extends Tunnel>> binaryEdgeRoots = CollectionsFactory.getMap();
52//
53// protected Address<? extends Tunnel> containmentRoot = null;
54// protected Address<? extends Supplier> containmentTransitiveRoot = null;
55// protected Address<? extends Tunnel> instantiationRoot = null;
56// protected Address<? extends Supplier> instantiationTransitiveRoot = null;
57// protected Address<? extends Tunnel> generalizationRoot = null;
58// protected Address<? extends Supplier> generalizationTransitiveRoot = null;
59
60
61 public InputConnector(Network network) {
62 super();
63 this.network = network;
64 }
65
66
67 public Network getNetwork() {
68 return network;
69 }
70
71
72 /**
73 * Connects a given input filter node to the external input source.
74 */
75 public void connectInputFilter(InputFilterRecipe recipe, Node freshNode) {
76 final ExternalInputStatelessFilterNode inputNode = (ExternalInputStatelessFilterNode)freshNode;
77
78 IInputKey inputKey = (IInputKey) recipe.getInputKey();
79 inputNode.connectThroughContext(network.getEngine(), inputKey);
80 }
81
82
83 /**
84 * Connects a given input enumerator node to the external input source.
85 */
86 public void connectInput(InputRecipe recipe, Node freshNode) {
87 final ExternalInputEnumeratorNode inputNode = (ExternalInputEnumeratorNode)freshNode;
88
89 IInputKey inputKey = (IInputKey) recipe.getInputKey();
90 Tuple seed = nopSeed(inputKey); // no preseeding as of now
91 final Address<ExternalInputEnumeratorNode> freshAddress = Address.of(inputNode);
92 externalInputRoots.computeIfAbsent(inputKey, k -> CollectionsFactory.createMap()).put(seed, freshAddress);
93 inputNode.connectThroughContext(network.getEngine(), inputKey, seed);
94
95// final Address<Tunnel> freshAddress = Address.of((Tunnel)freshNode);
96// if (recipe instanceof TypeInputRecipe) {
97// final Object typeKey = ((TypeInputRecipe) recipe).getTypeKey();
98//
99// if (recipe instanceof UnaryInputRecipe) {
100// unaryRoots.put(typeKey, freshAddress);
101// new EntityFeeder(freshAddress, this, typeKey).feed();
102//// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
103//// Collection<? extends Object> subTypes = context.enumerateDirectUnarySubtypes(typeObject);
104////
105//// for (Object subType : subTypes) {
106//// Address<? extends Tunnel> subRoot = accessUnaryRoot(subType);
107//// network.connectRemoteNodes(subRoot, tn, true);
108//// }
109//// }
110// } else if (recipe instanceof BinaryInputRecipe) {
111// binaryEdgeRoots.put(typeKey, freshAddress);
112// externalInputRoots.put(rowKey, columnKey, freshAddress);
113// new ReferenceFeeder(freshAddress, this, typeKey).feed();
114// // if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
115// // Collection<? extends Object> subTypes = context.enumerateDirectTernaryEdgeSubtypes(typeObject);
116// //
117// // for (Object subType : subTypes) {
118// // Address<? extends Tunnel> subRoot = accessTernaryEdgeRoot(subType);
119// // network.connectRemoteNodes(subRoot, tn, true);
120// // }
121// // }
122// }
123//
124//
125// }
126
127 }
128
129// /**
130// * fetches the entity Root node under specified label; returns null if it doesn't exist yet
131// */
132// public Address<? extends Tunnel> getUnaryRoot(Object label) {
133// return unaryRoots.get(label);
134// }
135//
136// public Collection<Address<? extends Tunnel>> getAllUnaryRoots() {
137// return unaryRoots.values();
138// }
139//
140// /**
141// * fetches the relation Root node under specified label; returns null if it doesn't exist yet
142// */
143// public Address<? extends Tunnel> getTernaryEdgeRoot(Object label) {
144// return ternaryEdgeRoots.get(label);
145// }
146//
147// public Collection<Address<? extends Tunnel>> getAllTernaryEdgeRoots() {
148// return ternaryEdgeRoots.values();
149// }
150//
151// /**
152// * fetches the reference Root node under specified label; returns null if it doesn't exist yet
153// */
154// public Address<? extends Tunnel> getBinaryEdgeRoot(Object label) {
155// return binaryEdgeRoots.get(label);
156// }
157//
158// public Collection<Address<? extends Tunnel>> getAllBinaryEdgeRoots() {
159// return binaryEdgeRoots.values();
160// }
161//
162//
163// public Address<? extends Tunnel> getContainmentRoot() {
164// return containmentRoot;
165// }
166//
167//
168// public Address<? extends Supplier> getContainmentTransitiveRoot() {
169// return containmentTransitiveRoot;
170// }
171//
172//
173// public Address<? extends Tunnel> getInstantiationRoot() {
174// return instantiationRoot;
175// }
176//
177//
178// public Address<? extends Supplier> getInstantiationTransitiveRoot() {
179// return instantiationTransitiveRoot;
180// }
181//
182//
183// public Address<? extends Tunnel> getGeneralizationRoot() {
184// return generalizationRoot;
185// }
186
187
188 public Stream<Address<ExternalInputEnumeratorNode>> getAllExternalInputNodes() {
189 return externalInputRoots.values().stream().flatMap(map -> map.values().stream());
190 }
191 public Collection<Address<ExternalInputEnumeratorNode>> getAllExternalInputNodesForKey(IInputKey inputKey) {
192 return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).values();
193 }
194 public Address<ExternalInputEnumeratorNode> getExternalInputNodeForKeyUnseeded(IInputKey inputKey) {
195 return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).get(nopSeed(inputKey));
196 }
197 public Address<ExternalInputEnumeratorNode> getExternalInputNode(IInputKey inputKey, Tuple seed) {
198 if (seed == null) seed = nopSeed(inputKey);
199 return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).get(seed);
200 }
201
202
203 Tuple nopSeed(IInputKey inputKey) {
204 return Tuples.flatTupleOf(new Object[inputKey.getArity()]);
205 }
206
207
208}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.boundary;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
17import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
18import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Direction;
22import tools.refinery.viatra.runtime.rete.matcher.ReteEngine;
23import tools.refinery.viatra.runtime.rete.network.Network;
24import tools.refinery.viatra.runtime.rete.network.ProductionNode;
25import tools.refinery.viatra.runtime.rete.network.Receiver;
26import tools.refinery.viatra.runtime.rete.network.ReteContainer;
27import tools.refinery.viatra.runtime.rete.network.Supplier;
28import tools.refinery.viatra.runtime.rete.remote.Address;
29import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery;
30import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
31
32/**
33 * Responsible for the storage, maintenance and communication of the nodes of the network that are accessible form the
34 * outside for various reasons.
35 *
36 * @author Gabor Bergmann
37 *
38 * <p> TODO: should eventually be merged into {@link InputConnector} and deleted
39 *
40 */
41public class ReteBoundary /*implements IPatternMatcherRuntimeContextListener*/ {
42
43 protected ReteEngine engine;
44 protected Network network;
45 protected ReteContainer headContainer;
46
47 public ReteContainer getHeadContainer() {
48 return headContainer;
49 }
50
51 protected final InputConnector inputConnector;
52
53
54 protected Map<SubPlan, Address<? extends Supplier>> subplanToAddressMapping;
55
56
57 /**
58 * SubPlans of parent nodes that have the key node as their child. For RETE --> SubPlan traceability, mainly at production
59 * nodes.
60 */
61 protected Map<Address<? extends Receiver>, Set<SubPlan>> parentPlansOfReceiver;
62
63 /**
64 * Prerequisite: engine has its network and framework fields initialized
65 */
66 public ReteBoundary(ReteEngine engine) {
67 super();
68 this.engine = engine;
69 this.network = engine.getReteNet();
70 this.headContainer = network.getHeadContainer();
71 inputConnector = network.getInputConnector();
72
73 this.parentPlansOfReceiver = CollectionsFactory.createMap();
74
75 // productionsScoped = new HashMap<GTPattern, Map<Map<Integer,Scope>,Address<? extends Production>>>();
76 subplanToAddressMapping = CollectionsFactory.createMap();
77
78 }
79
80 public Collection<? extends RecipeTraceInfo> getAllProductionNodes() {
81 return engine.getCompiler().getCachedCompiledQueries().values();
82 }
83
84// /**
85// * accesses the entity Root node under specified label; creates the node if it doesn't exist yet
86// */
87// public Address<? extends Tunnel> accessUnaryRoot(Object typeObject) {
88// Address<? extends Tunnel> tn;
89// tn = unaryRoots.get(typeObject);
90// if (tn == null) {
91// tn = headContainer.getProvisioner().newUniquenessEnforcerNode(1, typeObject);
92// unaryRoots.put(typeObject, tn);
93//
94// new EntityFeeder(tn, context, network, this, typeObject).feed();
95//
96// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
97// Collection<? extends Object> subTypes = context.enumerateDirectUnarySubtypes(typeObject);
98//
99// for (Object subType : subTypes) {
100// Address<? extends Tunnel> subRoot = accessUnaryRoot(subType);
101// network.connectRemoteNodes(subRoot, tn, true);
102// }
103// }
104//
105// }
106// return tn;
107// }
108//
109// /**
110// * accesses the relation Root node under specified label; creates the node if it doesn't exist yet
111// */
112// public Address<? extends Tunnel> accessTernaryEdgeRoot(Object typeObject) {
113// Address<? extends Tunnel> tn;
114// tn = ternaryEdgeRoots.get(typeObject);
115// if (tn == null) {
116// tn = headContainer.getProvisioner().newUniquenessEnforcerNode(3, typeObject);
117// ternaryEdgeRoots.put(typeObject, tn);
118//
119// new RelationFeeder(tn, context, network, this, typeObject).feed();
120//
121// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
122// Collection<? extends Object> subTypes = context.enumerateDirectTernaryEdgeSubtypes(typeObject);
123//
124// for (Object subType : subTypes) {
125// Address<? extends Tunnel> subRoot = accessTernaryEdgeRoot(subType);
126// network.connectRemoteNodes(subRoot, tn, true);
127// }
128// }
129// }
130// return tn;
131// }
132//
133// /**
134// * accesses the reference Root node under specified label; creates the node if it doesn't exist yet
135// */
136// public Address<? extends Tunnel> accessBinaryEdgeRoot(Object typeObject) {
137// Address<? extends Tunnel> tn;
138// tn = binaryEdgeRoots.get(typeObject);
139// if (tn == null) {
140// tn = headContainer.getProvisioner().newUniquenessEnforcerNode(2, typeObject);
141// binaryEdgeRoots.put(typeObject, tn);
142//
143// new ReferenceFeeder(tn, context, network, this, typeObject).feed();
144//
145// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) {
146// Collection<? extends Object> subTypes = context.enumerateDirectBinaryEdgeSubtypes(typeObject);
147//
148// for (Object subType : subTypes) {
149// Address<? extends Tunnel> subRoot = accessBinaryEdgeRoot(subType);
150// network.connectRemoteNodes(subRoot, tn, true);
151// }
152// }
153// }
154// return tn;
155// }
156//
157// /**
158// * accesses the special direct containment relation Root node; creates the node if it doesn't exist yet
159// */
160// public Address<? extends Tunnel> accessContainmentRoot() {
161// if (containmentRoot == null) {
162// // containment: relation quasi-type
163// containmentRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$containment");
164//
165// new ContainmentFeeder(containmentRoot, context, network, this).feed();
166// }
167// return containmentRoot;
168// }
169//
170// /**
171// * accesses the special transitive containment relation Root node; creates the node if it doesn't exist yet
172// */
173// public Address<? extends Supplier> accessContainmentTransitiveRoot() {
174// if (containmentTransitiveRoot == null) {
175// // transitive containment: derived
176// Address<? extends Tunnel> containmentTransitiveRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(
177// 2, "$containmentTransitive");
178// network.connectRemoteNodes(accessContainmentRoot(), containmentTransitiveRoot, true);
179//
180// final int[] actLI = { 1 };
181// final int arcLIw = 2;
182// final int[] actRI = { 0 };
183// final int arcRIw = 2;
184// Address<? extends IterableIndexer> jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer(
185// accessContainmentRoot(), new TupleMask(actLI, arcLIw));
186// Address<? extends IterableIndexer> jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer(
187// containmentTransitiveRoot, new TupleMask(actRI, arcRIw));
188//
189// final int[] actRIcomp = { 1 };
190// final int arcRIwcomp = 2;
191// TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp);
192//
193// Address<? extends Supplier> andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot,
194// complementerMask);
195//
196// final int[] mask = { 0, 2 };
197// final int maskw = 3;
198// Address<? extends Supplier> tr = headContainer.getProvisioner().accessTrimmerNode(andCT, new TupleMask(mask, maskw));
199// network.connectRemoteNodes(tr, containmentTransitiveRoot, true);
200//
201// this.containmentTransitiveRoot = containmentTransitiveRoot; // cast
202// // back
203// // to
204// // Supplier
205// }
206// return containmentTransitiveRoot;
207// }
208//
209// /**
210// * accesses the special instantiation relation Root node; creates the node if it doesn't exist yet
211// */
212// public Address<? extends Tunnel> accessInstantiationRoot() {
213// if (instantiationRoot == null) {
214// // instantiation: relation quasi-type
215// instantiationRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$instantiation");
216//
217// new InstantiationFeeder(instantiationRoot, context, network, this).feed();
218// }
219// return instantiationRoot;
220// }
221//
222// /**
223// * accesses the special transitive instantiation relation Root node; creates the node if it doesn't exist yet
224// * InstantiationTransitive = Instantiation o (Generalization)^*
225// */
226// public Address<? extends Supplier> accessInstantiationTransitiveRoot() {
227// if (instantiationTransitiveRoot == null) {
228// // transitive instantiation: derived
229// Address<? extends Tunnel> instantiationTransitiveRoot = headContainer.getProvisioner()
230// .newUniquenessEnforcerNode(2, "$instantiationTransitive");
231// network.connectRemoteNodes(accessInstantiationRoot(), instantiationTransitiveRoot, true);
232//
233// final int[] actLI = { 1 };
234// final int arcLIw = 2;
235// final int[] actRI = { 0 };
236// final int arcRIw = 2;
237// Address<? extends IterableIndexer> jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer(
238// accessGeneralizationRoot(), new TupleMask(actLI, arcLIw));
239// Address<? extends Indexer> jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer(
240// instantiationTransitiveRoot, new TupleMask(actRI, arcRIw));
241//
242// final int[] actRIcomp = { 1 };
243// final int arcRIwcomp = 2;
244// TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp);
245//
246// Address<? extends Supplier> andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot,
247// complementerMask);
248//
249// final int[] mask = { 0, 2 };
250// final int maskw = 3;
251// Address<? extends Supplier> tr = headContainer.getProvisioner().accessTrimmerNode(andCT,
252// new TupleMask(mask, maskw));
253// network.connectRemoteNodes(tr, instantiationTransitiveRoot, true);
254//
255// this.instantiationTransitiveRoot = instantiationTransitiveRoot; // cast
256// // back
257// // to
258// // Supplier
259// }
260// return instantiationTransitiveRoot;
261// }
262//
263// /**
264// * accesses the special generalization relation Root node; creates the node if it doesn't exist yet
265// */
266// public Address<? extends Tunnel> accessGeneralizationRoot() {
267// if (generalizationRoot == null) {
268// // generalization: relation quasi-type
269// generalizationRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$generalization");
270//
271// new GeneralizationFeeder(generalizationRoot, context, network, this).feed();
272// }
273// return generalizationRoot;
274// }
275//
276// /**
277// * accesses the special transitive containment relation Root node; creates the node if it doesn't exist yet
278// */
279// public Address<? extends Supplier> accessGeneralizationTransitiveRoot() {
280// if (generalizationTransitiveRoot == null) {
281// // transitive generalization: derived
282// Address<? extends Tunnel> generalizationTransitiveRoot = headContainer.getProvisioner()
283// .newUniquenessEnforcerNode(2, "$generalizationTransitive");
284// network.connectRemoteNodes(accessGeneralizationRoot(), generalizationTransitiveRoot, true);
285//
286// final int[] actLI = { 1 };
287// final int arcLIw = 2;
288// final int[] actRI = { 0 };
289// final int arcRIw = 2;
290// Address<? extends IterableIndexer> jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer(
291// accessGeneralizationRoot(), new TupleMask(actLI, arcLIw));
292// Address<? extends Indexer> jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer(
293// generalizationTransitiveRoot, new TupleMask(actRI, arcRIw));
294//
295// final int[] actRIcomp = { 1 };
296// final int arcRIwcomp = 2;
297// TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp);
298//
299// Address<? extends Supplier> andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot,
300// complementerMask);
301//
302// final int[] mask = { 0, 2 };
303// final int maskw = 3;
304// Address<? extends Supplier> tr = headContainer.getProvisioner().accessTrimmerNode(andCT, new TupleMask(mask, maskw));
305// network.connectRemoteNodes(tr, generalizationTransitiveRoot, true);
306//
307// this.generalizationTransitiveRoot = generalizationTransitiveRoot; // cast
308// // back
309// // to
310// // Supplier
311// }
312// return generalizationTransitiveRoot;
313// }
314
315 // /**
316 // * Registers and publishes a supplier under specified label.
317 // */
318 // public void publishSupplier(Supplier s, Object label)
319 // {
320 // publishedSuppliers.put(label, s);
321 // }
322 //
323 // /**
324 // * fetches the production node under specified label;
325 // * returns null if it doesn't exist yet
326 // */
327 // public Production getProductionNode(Object label)
328 // {
329 // return productions.get(label);
330 // }
331 //
332 // /**
333 // * fetches the published supplier under specified label;
334 // * returns null if it doesn't exist yet
335 // */
336 // public Supplier getPublishedSupplier(Object label)
337 // {
338 // return publishedSuppliers.get(label);
339 // }
340
341 /**
342 * accesses the production node for specified pattern; builds pattern matcher if it doesn't exist yet
343 * @throws ViatraQueryRuntimeException
344 */
345 public synchronized RecipeTraceInfo accessProductionTrace(PQuery query)
346 {
347 final CompiledQuery compiled = engine.getCompiler().getCompiledForm(query);
348 return compiled;
349// RecipeTraceInfo pn;
350// pn = queryPlans.get(query);
351// if (pn == null) {
352// pn = construct(query);
353// TODO handle recursion by reinterpret-RecipeTrace
354// queryPlans.put(query, pn);
355// if (pn == null) {
356// String[] args = { query.toString() };
357// throw new RetePatternBuildException("Unsuccessful planning of RETE construction recipe for query {1}",
358// args, "Could not create RETE recipe plan.", query);
359// }
360// }
361// return pn;
362 }
363 /**
364 * accesses the production node for specified pattern; builds pattern matcher if it doesn't exist yet
365 * @throws ViatraQueryRuntimeException
366 */
367 public synchronized Address<? extends ProductionNode> accessProductionNode(PQuery query) {
368 final RecipeTraceInfo productionTrace = accessProductionTrace(query);
369 return (Address<? extends ProductionNode>) headContainer.getProvisioner().getOrCreateNodeByRecipe(productionTrace);
370 }
371
372// /**
373// * creates the production node for the specified pattern Contract: only call from the builder (through Buildable)
374// * responsible for building this pattern
375// *
376// * @throws PatternMatcherCompileTimeException
377// * if production node is already created
378// */
379// public synchronized Address<? extends Production> createProductionInternal(PQuery gtPattern)
380// throws QueryPlannerException {
381// if (queryPlans.containsKey(gtPattern)) {
382// String[] args = { gtPattern.toString() };
383// throw new RetePatternBuildException("Multiple creation attempts of production node for {1}", args,
384// "Duplicate RETE production node.", gtPattern);
385// }
386//
387// Map<String, Integer> posMapping = engine.getBuilder().getPosMapping(gtPattern);
388// Address<? extends Production> pn = headContainer.getProvisioner().newProductionNode(posMapping, gtPattern);
389// queryPlans.put(gtPattern, pn);
390// context.reportPatternDependency(gtPattern);
391//
392// return pn;
393// }
394
395 // /**
396 // * accesses the production node for specified pattern and scope map; creates the node if
397 // * it doesn't exist yet
398 // */
399 // public synchronized Address<? extends Production> accessProductionScoped(
400 // GTPattern gtPattern, Map<Integer, Scope> additionalScopeMap) throws PatternMatcherCompileTimeException {
401 // if (additionalScopeMap.isEmpty()) return accessProduction(gtPattern);
402 //
403 // Address<? extends Production> pn;
404 //
405 // Map<Map<Integer, Scope>, Address<? extends Production>> scopes = productionsScoped.get(gtPattern);
406 // if (scopes == null) {
407 // scopes = new HashMap<Map<Integer, Scope>, Address<? extends Production>>();
408 // productionsScoped.put(gtPattern, scopes);
409 // }
410 //
411 // pn = scopes.get(additionalScopeMap);
412 // if (pn == null) {
413 // Address<? extends Production> unscopedProduction = accessProduction(gtPattern);
414 //
415 // HashMap<Object, Integer> posMapping = headContainer.resolveLocal(unscopedProduction).getPosMapping();
416 // pn = headContainer.getLibrary().newProductionNode(posMapping);
417 // scopes.put(additionalScopeMap, pn);
418 //
419 // constructScoper(unscopedProduction, additionalScopeMap, pn);
420 // }
421 // return pn;
422 // }
423
424 // protected void constructScoper(
425 // Address<? extends Production> unscopedProduction,
426 // Map<Integer, Scope> additionalScopeMap,
427 // Address<? extends Production> production)
428 // throws PatternMatcherCompileTimeException {
429 // engine.reteNet.waitForReteTermination();
430 // engine.builder.constructScoper(unscopedProduction, additionalScopeMap, production);
431 // }
432
433 // /**
434 // * Invalidates the subnet constructed for the recognition of a given
435 // pattern.
436 // * The pattern matcher will have to be rebuilt.
437 // * @param gtPattern the pattern whose matcher subnet should be invalidated
438 // */
439 // public void invalidatePattern(GTPattern gtPattern) {
440 // Production production = null;
441 // try {
442 // production = accessProduction(gtPattern);
443 // } catch (PatternMatcherCompileTimeException e) {
444 // // this should not occur here, since we already have a production node
445 // e.printStackTrace();
446 // }
447 //
448 // production.tearOff();
449 // //production.setDirty(true);
450 // }
451
452 // updaters for change notification
453 // if the corresponding rete input isn't created yet, call is ignored
454
455 private static Direction direction(boolean isInsertion) {
456 return isInsertion ? Direction.INSERT : Direction.DELETE;
457 }
458
459// @Override
460// public void updateUnary(boolean isInsertion, Object entity, Object typeObject) {
461// Address<? extends Tunnel> root = inputConnector.getUnaryRoot(typeObject);
462// if (root != null) {
463// network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(entity)));
464// if (!engine.isParallelExecutionEnabled())
465// network.waitForReteTermination();
466// }
467// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) {
468// for (Object superType : context.enumerateDirectUnarySupertypes(typeObject)) {
469// updateUnary(isInsertion, entity, superType);
470// }
471// }
472// }
473//
474// @Override
475// public void updateTernaryEdge(boolean isInsertion, Object relation, Object from, Object to, Object typeObject) {
476// Address<? extends Tunnel> root = inputConnector.getTernaryEdgeRoot(typeObject);
477// if (root != null) {
478// network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(relation), inputConnector.wrapElement(from),
479// inputConnector.wrapElement(to)));
480// if (!engine.isParallelExecutionEnabled())
481// network.waitForReteTermination();
482// }
483// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) {
484// for (Object superType : context.enumerateDirectTernaryEdgeSupertypes(typeObject)) {
485// updateTernaryEdge(isInsertion, relation, from, to, superType);
486// }
487// }
488// }
489// @Override
490// public void updateBinaryEdge(boolean isInsertion, Object from, Object to, Object typeObject) {
491// Address<? extends Tunnel> root = inputConnector.getBinaryEdgeRoot(typeObject);
492// if (root != null) {
493// network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(from), inputConnector.wrapElement(to)));
494// if (!engine.isParallelExecutionEnabled())
495// network.waitForReteTermination();
496// }
497// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) {
498// for (Object superType : context.enumerateDirectBinaryEdgeSupertypes(typeObject)) {
499// updateBinaryEdge(isInsertion, from, to, superType);
500// }
501// }
502// }
503//
504// @Override
505// public void updateContainment(boolean isInsertion, Object container, Object element) {
506// final Address<? extends Tunnel> containmentRoot = inputConnector.getContainmentRoot();
507// if (containmentRoot != null) {
508// network.sendExternalUpdate(containmentRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(container),
509// inputConnector.wrapElement(element)));
510// if (!engine.isParallelExecutionEnabled())
511// network.waitForReteTermination();
512// }
513// }
514//
515// @Override
516// public void updateInstantiation(boolean isInsertion, Object parent, Object child) {
517// final Address<? extends Tunnel> instantiationRoot = inputConnector.getInstantiationRoot();
518// if (instantiationRoot != null) {
519// network.sendExternalUpdate(instantiationRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(parent),
520// inputConnector.wrapElement(child)));
521// if (!engine.isParallelExecutionEnabled())
522// network.waitForReteTermination();
523// }
524// }
525//
526// @Override
527// public void updateGeneralization(boolean isInsertion, Object parent, Object child) {
528// final Address<? extends Tunnel> generalizationRoot = inputConnector.getGeneralizationRoot();
529// if (generalizationRoot != null) {
530// network.sendExternalUpdate(generalizationRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(parent),
531// inputConnector.wrapElement(child)));
532// if (!engine.isParallelExecutionEnabled())
533// network.waitForReteTermination();
534// }
535// }
536
537 // no wrapping needed!
538 public void notifyEvaluator(Address<? extends Receiver> receiver, Tuple tuple) {
539 network.sendExternalUpdate(receiver, Direction.INSERT, tuple);
540 if (!engine.isParallelExecutionEnabled())
541 network.waitForReteTermination();
542 }
543
544 public void mapPlanToAddress(SubPlan plan, Address<? extends Supplier> handle) {
545 subplanToAddressMapping.put(plan, handle);
546 }
547
548 public Address<? extends Supplier> getAddress(SubPlan plan) {
549 return subplanToAddressMapping.get(plan);
550 }
551}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.construction;
11
12import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException;
13
14/**
15 * A problem has occurred during the construction of the RETE net.
16 *
17 * @author Gabor Bergmann
18 *
19 */
20public class RetePatternBuildException extends QueryProcessingException {
21
22 private static final long serialVersionUID = 6966585498204577548L;
23
24 /**
25 * @param message
26 * The template of the exception message
27 * @param context
28 * The data elements to be used to instantiate the template. Can be null if no context parameter is
29 * defined
30 * @param patternDescription
31 * the PatternDescription where the exception occurred
32 */
33 public RetePatternBuildException(String message, String[] context, String shortMessage, Object patternDescription) {
34 super(message, context, shortMessage, patternDescription);
35 }
36
37 /**
38 * @param message
39 * The template of the exception message
40 * @param context
41 * The data elements to be used to instantiate the template. Can be null if no context parameter is
42 * defined
43 * @param patternDescription
44 * the PatternDescription where the exception occurred
45 */
46 public RetePatternBuildException(String message, String[] context, String shortMessage, Object patternDescription,
47 Throwable cause) {
48 super(message, context, shortMessage, patternDescription, cause);
49 }
50}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.construction.basiclinear;
11
12import java.util.Arrays;
13import java.util.Collections;
14import java.util.Set;
15
16import org.apache.log4j.Logger;
17import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
18import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
19import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
20import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy;
21import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
22import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory;
23import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper;
24import tools.refinery.viatra.runtime.matchers.planning.operations.PApply;
25import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
26import tools.refinery.viatra.runtime.matchers.planning.operations.PStart;
27import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint;
28import tools.refinery.viatra.runtime.matchers.psystem.PBody;
29import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
30import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
31import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint;
32import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality;
33import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter;
34import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation;
35import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
36import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
37import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException;
38
39/**
40 * Basic layout that builds a linear RETE net based on a heuristic ordering of constraints.
41 *
42 * @author Gabor Bergmann
43 *
44 */
45public class BasicLinearLayout implements IQueryPlannerStrategy {
46
47 //SubPlanProcessor planProcessor = new SubPlanProcessor();
48
49 private IQueryBackendHintProvider hintProvider;
50 private IQueryBackendContext bContext;
51 /**
52 * @param bContext
53 * @since 1.5
54 */
55 public BasicLinearLayout(IQueryBackendContext bContext) {
56 this.bContext = bContext;
57 this.hintProvider = bContext.getHintProvider();
58 }
59
60 @Override
61 public SubPlan plan(final PBody pSystem, Logger logger, IQueryMetaContext context) {
62 SubPlanFactory planFactory = new SubPlanFactory(pSystem);
63 PQuery query = pSystem.getPattern();
64 //planProcessor.setCompiler(compiler);
65 try {
66 logger.debug(String.format(
67 "%s: patternbody build started for %s",
68 getClass().getSimpleName(),
69 query.getFullyQualifiedName()));
70
71 // STARTING THE LINE
72 SubPlan plan = planFactory.createSubPlan(new PStart());
73
74 Set<PConstraint> pQueue = CollectionsFactory.createSet(pSystem.getConstraints());
75
76 // MAIN LOOP
77 while (!pQueue.isEmpty()) {
78 PConstraint pConstraint = Collections.min(pQueue,
79 new OrderingHeuristics(plan, context)); // pQueue.iterator().next();
80 pQueue.remove(pConstraint);
81
82 // if we have no better option than an unready deferred constraint, raise error
83 if (pConstraint instanceof DeferredPConstraint) {
84 final DeferredPConstraint deferred = (DeferredPConstraint) pConstraint;
85 if (!deferred.isReadyAt(plan, context)) {
86 raiseForeverDeferredError(deferred, plan, context);
87 }
88 }
89 // TODO integrate the check above in SubPlan / POperation??
90
91 // replace incumbent plan with its child
92 plan = planFactory.createSubPlan(new PApply(pConstraint), plan);
93 }
94
95 // PROJECT TO PARAMETERS
96 SubPlan finalPlan = planFactory.createSubPlan(new PProject(pSystem.getSymbolicParameterVariables()), plan);
97
98 // FINAL CHECK, whether all exported variables are present + all constraint satisfied
99 BuildHelper.finalCheck(pSystem, finalPlan, context);
100 // TODO integrate the check above in SubPlan / POperation
101
102 logger.debug(String.format(
103 "%s: patternbody query plan concluded for %s as: %s",
104 getClass().getSimpleName(),
105 query.getFullyQualifiedName(),
106 finalPlan.toLongString()));
107
108 return finalPlan;
109
110 } catch (RetePatternBuildException ex) {
111 ex.setPatternDescription(query);
112 throw ex;
113 }
114 }
115
116 /**
117 * Called when the constraint is not ready, but cannot be deferred further.
118 *
119 * @param plan
120 * @throws RetePatternBuildException
121 * to indicate the error in detail.
122 */
123 private void raiseForeverDeferredError(DeferredPConstraint constraint, SubPlan plan, IQueryMetaContext context) {
124 if (constraint instanceof Equality) {
125 raiseForeverDeferredError((Equality)constraint, plan, context);
126 } else if (constraint instanceof ExportedParameter) {
127 raiseForeverDeferredError((ExportedParameter)constraint, plan, context);
128 } else if (constraint instanceof ExpressionEvaluation) {
129 raiseForeverDeferredError((ExpressionEvaluation)constraint, plan, context);
130 } else if (constraint instanceof VariableDeferredPConstraint) {
131 raiseForeverDeferredError(constraint, plan, context);
132 }
133 }
134
135 private void raiseForeverDeferredError(Equality constraint, SubPlan plan, IQueryMetaContext context) {
136 String[] args = { constraint.getWho().toString(), constraint.getWithWhom().toString() };
137 String msg = "Cannot express equality of variables {1} and {2} if neither of them is deducable.";
138 String shortMsg = "Equality between undeducible variables.";
139 throw new RetePatternBuildException(msg, args, shortMsg, null);
140 }
141 private void raiseForeverDeferredError(ExportedParameter constraint, SubPlan plan, IQueryMetaContext context) {
142 String[] args = { constraint.getParameterName() };
143 String msg = "Pattern Graph Search terminated incompletely: "
144 + "exported pattern variable {1} could not be determined based on the pattern constraints. "
145 + "HINT: certain constructs (e.g. negative patterns or check expressions) cannot output symbolic parameters.";
146 String shortMsg = "Could not deduce value of parameter";
147 throw new RetePatternBuildException(msg, args, shortMsg, null);
148 }
149 private void raiseForeverDeferredError(ExpressionEvaluation constraint, SubPlan plan, IQueryMetaContext context) {
150 if (constraint.checkTypeSafety(plan, context) == null) {
151 raiseForeverDeferredError(constraint, plan);
152 } else {
153 String[] args = { toString(), constraint.checkTypeSafety(plan, context).toString() };
154 String msg = "The checking of pattern constraint {1} cannot be deferred further, but variable {2} is still not type safe. "
155 + "HINT: the incremental matcher is not an equation solver, please make sure that all variable values are deducible.";
156 String shortMsg = "Could not check all constraints due to undeducible type restrictions";
157 throw new RetePatternBuildException(msg, args, shortMsg, null);
158 }
159 }
160 private void raiseForeverDeferredError(VariableDeferredPConstraint constraint, SubPlan plan) {
161 Set<PVariable> missing = CollectionsFactory.createSet(constraint.getDeferringVariables());//new HashSet<PVariable>(getDeferringVariables());
162 missing.removeAll(plan.getVisibleVariables());
163 String[] args = { toString(), Arrays.toString(missing.toArray()) };
164 String msg = "The checking of pattern constraint {1} requires the values of variables {2}, but it cannot be deferred further. "
165 + "HINT: the incremental matcher is not an equation solver, please make sure that all variable values are deducible.";
166 String shortMsg = "Could not check all constraints due to undeducible variables";
167 throw new RetePatternBuildException(msg, args, shortMsg, null);
168 }
169
170
171}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.construction.basiclinear;
11
12import java.util.Comparator;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
16import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
17import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint;
18import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint;
19import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
20import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
21import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
23import tools.refinery.viatra.runtime.rete.util.OrderingCompareAgent;
24
25/**
26 * @author Gabor Bergmann
27 *
28 */
29public class OrderingHeuristics implements Comparator<PConstraint> {
30 private SubPlan plan;
31 private IQueryMetaContext context;
32
33 public OrderingHeuristics(SubPlan plan, IQueryMetaContext context) {
34 super();
35 this.plan = plan;
36 this.context = context;
37 }
38
39 @Override
40 public int compare(PConstraint o1, PConstraint o2) {
41 return new OrderingCompareAgent<PConstraint>(o1, o2) {
42 @Override
43 protected void doCompare() {
44 boolean temp = consider(preferTrue(isConstant(a), isConstant(b)))
45 && consider(preferTrue(isReady(a), isReady(b)));
46 if (!temp)
47 return;
48
49 Set<PVariable> bound1 = boundVariables(a);
50 Set<PVariable> bound2 = boundVariables(b);
51 swallowBoolean(temp && consider(preferTrue(isBound(a, bound1), isBound(b, bound2)))
52 && consider(preferMore(degreeBound(a, bound1), degreeBound(b, bound2)))
53 && consider(preferLess(degreeFree(a, bound1), degreeFree(b, bound2)))
54
55 // tie breaking
56 && consider(preferLess(a.getMonotonousID(), b.getMonotonousID())) // this is hopefully deterministic
57 && consider(preferLess(System.identityHashCode(a), System.identityHashCode(b))));
58 }
59 }.compare();
60 }
61
62 boolean isConstant(PConstraint o) {
63 return (o instanceof ConstantValue);
64 }
65
66 boolean isReady(PConstraint o) {
67 return (o instanceof EnumerablePConstraint)
68 || (o instanceof DeferredPConstraint && ((DeferredPConstraint) o)
69 .isReadyAt(plan, context));
70 }
71
72 Set<PVariable> boundVariables(PConstraint o) {
73 Set<PVariable> boundVariables = CollectionsFactory.createSet(o.getAffectedVariables());
74 boundVariables.retainAll(plan.getVisibleVariables());
75 return boundVariables;
76 }
77
78 boolean isBound(PConstraint o, Set<PVariable> boundVariables) {
79 return boundVariables.size() == o.getAffectedVariables().size();
80 }
81
82 int degreeBound(PConstraint o, Set<PVariable> boundVariables) {
83 return boundVariables.size();
84 }
85
86 int degreeFree(PConstraint o, Set<PVariable> boundVariables) {
87 return o.getAffectedVariables().size() - boundVariables.size();
88 }
89
90}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.construction.plancompiler;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.LinkedHashSet;
16import java.util.LinkedList;
17import java.util.List;
18import java.util.Map;
19import java.util.Map.Entry;
20import java.util.Set;
21import java.util.SortedSet;
22import java.util.TreeSet;
23
24import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
25import tools.refinery.viatra.runtime.matchers.context.IInputKey;
26import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
27import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
28import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
29import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper;
30import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint;
31import tools.refinery.viatra.runtime.matchers.psystem.PBody;
32import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
33import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
34import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
35import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
36import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
37import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
38import tools.refinery.viatra.runtime.rete.recipes.EqualityFilterRecipe;
39import tools.refinery.viatra.runtime.rete.recipes.IndexerBasedAggregatorRecipe;
40import tools.refinery.viatra.runtime.rete.recipes.IndexerRecipe;
41import tools.refinery.viatra.runtime.rete.recipes.JoinRecipe;
42import tools.refinery.viatra.runtime.rete.recipes.Mask;
43import tools.refinery.viatra.runtime.rete.recipes.MonotonicityInfo;
44import tools.refinery.viatra.runtime.rete.recipes.ProductionRecipe;
45import tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe;
46import tools.refinery.viatra.runtime.rete.recipes.RecipesFactory;
47import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
48import tools.refinery.viatra.runtime.rete.recipes.SingleColumnAggregatorRecipe;
49import tools.refinery.viatra.runtime.rete.recipes.TrimmerRecipe;
50import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper;
51import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery;
52import tools.refinery.viatra.runtime.rete.traceability.CompiledSubPlan;
53import tools.refinery.viatra.runtime.rete.traceability.PlanningTrace;
54import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
55import tools.refinery.viatra.runtime.rete.util.ReteHintOptions;
56
57/**
58 * @author Bergmann Gabor
59 *
60 */
61public class CompilerHelper {
62
63 private CompilerHelper() {/*Utility class constructor*/}
64
65 static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE;
66
67 /**
68 * Makes sure that all variables in the tuple are different so that it can be used as {@link CompiledSubPlan}. If a
69 * variable occurs multiple times, equality checks are applied and then the results are trimmed so that duplicates
70 * are hidden. If no manipulation is necessary, the original trace is returned.
71 *
72 * <p>
73 * to be used whenever a constraint introduces new variables.
74 */
75 public static PlanningTrace checkAndTrimEqualVariables(SubPlan plan, final PlanningTrace coreTrace) {
76 // are variables in the constraint all different?
77 final List<PVariable> coreVariablesTuple = coreTrace.getVariablesTuple();
78 final int constraintArity = coreVariablesTuple.size();
79 final int distinctVariables = coreTrace.getPosMapping().size();
80 if (constraintArity == distinctVariables) {
81 // all variables occur exactly once in tuple
82 return coreTrace;
83 } else { // apply equality checks and trim
84
85 // find the positions in the tuple for each variable
86 Map<PVariable, SortedSet<Integer>> posMultimap = new HashMap<PVariable, SortedSet<Integer>>();
87 List<PVariable> trimmedVariablesTuple = new ArrayList<PVariable>(distinctVariables);
88 int[] trimIndices = new int[distinctVariables];
89 for (int i = 0; i < constraintArity; ++i) {
90 final PVariable variable = coreVariablesTuple.get(i);
91 SortedSet<Integer> indexSet = posMultimap.get(variable);
92 if (indexSet == null) { // first occurrence of variable
93 indexSet = new TreeSet<Integer>();
94 posMultimap.put(variable, indexSet);
95
96 // this is the first occurrence, set up trimming
97 trimIndices[trimmedVariablesTuple.size()] = i;
98 trimmedVariablesTuple.add(variable);
99 }
100 indexSet.add(i);
101 }
102
103 // construct equality checks for each variable occurring multiple times
104 PlanningTrace lastTrace = coreTrace;
105 for (Entry<PVariable, SortedSet<Integer>> entry : posMultimap.entrySet()) {
106 if (entry.getValue().size() > 1) {
107 EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe();
108 equalityFilterRecipe.setParent(lastTrace.getRecipe());
109 equalityFilterRecipe.getIndices().addAll(entry.getValue());
110 lastTrace = new PlanningTrace(plan, coreVariablesTuple, equalityFilterRecipe, lastTrace);
111 }
112 }
113
114 // trim so that each variable occurs only once
115 TrimmerRecipe trimmerRecipe = FACTORY.createTrimmerRecipe();
116 trimmerRecipe.setParent(lastTrace.getRecipe());
117 trimmerRecipe.setMask(tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper
118 .mask(constraintArity, trimIndices));
119 return new PlanningTrace(plan, trimmedVariablesTuple, trimmerRecipe, lastTrace);
120 }
121 }
122
123 /**
124 * Extracts the variable list representation of the variables tuple.
125 */
126 public static List<PVariable> convertVariablesTuple(EnumerablePConstraint constraint) {
127 return convertVariablesTuple(constraint.getVariablesTuple());
128 }
129
130 /**
131 * Extracts the variable list representation of the variables tuple.
132 */
133 public static List<PVariable> convertVariablesTuple(Tuple variablesTuple) {
134 List<PVariable> result = new ArrayList<PVariable>();
135 for (Object o : variablesTuple.getElements())
136 result.add((PVariable) o);
137 return result;
138 }
139
140 /**
141 * Returns a compiled indexer trace according to a mask
142 */
143 public static RecipeTraceInfo makeIndexerTrace(SubPlan planToCompile, PlanningTrace parentTrace, TupleMask mask) {
144 final ReteNodeRecipe parentRecipe = parentTrace.getRecipe();
145 if (parentRecipe instanceof IndexerBasedAggregatorRecipe
146 || parentRecipe instanceof SingleColumnAggregatorRecipe)
147 throw new IllegalArgumentException(
148 "Cannot take projection indexer of aggregator node at plan " + planToCompile);
149 IndexerRecipe recipe = RecipesHelper.projectionIndexerRecipe(parentRecipe, toRecipeMask(mask));
150 // final List<PVariable> maskedVariables = mask.transform(parentTrace.getVariablesTuple());
151 return new PlanningTrace(planToCompile, /* maskedVariables */ parentTrace.getVariablesTuple(), recipe,
152 parentTrace);
153 // TODO add specialized indexer trace info?
154 }
155
156 /**
157 * Creates a trimmer that keeps selected variables only.
158 */
159 protected static TrimmerRecipe makeTrimmerRecipe(final PlanningTrace compiledParent,
160 List<PVariable> projectedVariables) {
161 final Mask projectionMask = makeProjectionMask(compiledParent, projectedVariables);
162 final TrimmerRecipe trimmerRecipe = ReteRecipeCompiler.FACTORY.createTrimmerRecipe();
163 trimmerRecipe.setParent(compiledParent.getRecipe());
164 trimmerRecipe.setMask(projectionMask);
165 return trimmerRecipe;
166 }
167
168 public static Mask makeProjectionMask(final PlanningTrace compiledParent, Iterable<PVariable> projectedVariables) {
169 List<Integer> projectionSourceIndices = new ArrayList<Integer>();
170 for (PVariable pVariable : projectedVariables) {
171 projectionSourceIndices.add(compiledParent.getPosMapping().get(pVariable));
172 }
173 final Mask projectionMask = RecipesHelper.mask(compiledParent.getRecipe().getArity(), projectionSourceIndices);
174 return projectionMask;
175 }
176
177 /**
178 * @since 1.6
179 */
180 public static final class PosetTriplet {
181 public Mask coreMask;
182 public Mask posetMask;
183 public IPosetComparator comparator;
184 }
185
186 /**
187 * @since 1.6
188 */
189 public static PosetTriplet computePosetInfo(List<PVariable> variables, PBody body, IQueryMetaContext context) {
190 Map<PVariable, Set<IInputKey>> typeMap = TypeHelper.inferUnaryTypesFor(variables, body.getConstraints(),
191 context);
192 List<Set<IInputKey>> keys = new LinkedList<Set<IInputKey>>();
193
194 for (int i = 0; i < variables.size(); i++) {
195 keys.add(typeMap.get(variables.get(i)));
196 }
197
198 return computePosetInfo(keys, context);
199 }
200
201 /**
202 * @since 1.6
203 */
204 public static PosetTriplet computePosetInfo(List<PParameter> parameters, IQueryMetaContext context) {
205 List<Set<IInputKey>> keys = new LinkedList<Set<IInputKey>>();
206 for (int i = 0; i < parameters.size(); i++) {
207 IInputKey key = parameters.get(i).getDeclaredUnaryType();
208 if (key == null) {
209 keys.add(Collections.emptySet());
210 } else {
211 keys.add(Collections.singleton(parameters.get(i).getDeclaredUnaryType()));
212 }
213 }
214 return computePosetInfo(keys, context);
215 }
216
217
218
219 /**
220 * @since 1.6
221 */
222 public static PosetTriplet computePosetInfo(Iterable<Set<IInputKey>> keys, IQueryMetaContext context) {
223 PosetTriplet result = new PosetTriplet();
224 List<Integer> coreIndices = new ArrayList<Integer>();
225 List<Integer> posetIndices = new ArrayList<Integer>();
226 List<IInputKey> filtered = new ArrayList<IInputKey>();
227 boolean posetKey = false;
228 int index = -1;
229
230 for (Set<IInputKey> _keys : keys) {
231 ++index;
232 posetKey = false;
233
234 for (IInputKey key : _keys) {
235 if (key != null && context.isPosetKey(key)) {
236 posetKey = true;
237 filtered.add(key);
238 break;
239 }
240 }
241
242 if (posetKey) {
243 posetIndices.add(index);
244 } else {
245 coreIndices.add(index);
246 }
247 }
248
249 result.comparator = context.getPosetComparator(filtered);
250 result.coreMask = RecipesHelper.mask(index + 1, coreIndices);
251 result.posetMask = RecipesHelper.mask(index + 1, posetIndices);
252
253 return result;
254 }
255
256 /**
257 * Creates a recipe for a production node and the corresponding trace.
258 * <p> PRE: in case this is a recursion cutoff point (see {@link RecursionCutoffPoint})
259 * and bodyFinalTraces will be filled later,
260 * the object yielded now by bodyFinalTraces.values() must return up-to-date results later
261 * @since 2.4
262 */
263 public static CompiledQuery makeQueryTrace(PQuery query, Map<PBody, RecipeTraceInfo> bodyFinalTraces,
264 Collection<ReteNodeRecipe> bodyFinalRecipes, QueryEvaluationHint hint, IQueryMetaContext context,
265 boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) {
266 ProductionRecipe recipe = ReteRecipeCompiler.FACTORY.createProductionRecipe();
267
268 // temporary solution to support the deprecated option for now
269 boolean deleteAndRederiveEvaluationDep = deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(hint);
270
271 recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep);
272
273 if (deleteAndRederiveEvaluationDep || (timelyEvaluation != null)) {
274 PosetTriplet triplet = computePosetInfo(query.getParameters(), context);
275 if (triplet.comparator != null) {
276 MonotonicityInfo info = FACTORY.createMonotonicityInfo();
277 info.setCoreMask(triplet.coreMask);
278 info.setPosetMask(triplet.posetMask);
279 info.setPosetComparator(triplet.comparator);
280 recipe.setOptionalMonotonicityInfo(info);
281 }
282 }
283
284 recipe.setPattern(query);
285 recipe.setPatternFQN(query.getFullyQualifiedName());
286 recipe.setTraceInfo(recipe.getPatternFQN());
287 recipe.getParents().addAll(bodyFinalRecipes);
288 for (int i = 0; i < query.getParameterNames().size(); ++i) {
289 recipe.getMappedIndices().put(query.getParameterNames().get(i), i);
290 }
291
292 return new CompiledQuery(recipe, bodyFinalTraces, query);
293 }
294
295 /**
296 * Calculated index mappings for a join, based on the common variables of the two parent subplans.
297 *
298 * @author Gabor Bergmann
299 *
300 */
301 public static class JoinHelper {
302 private TupleMask primaryMask;
303 private TupleMask secondaryMask;
304 private TupleMask complementerMask;
305 private RecipeTraceInfo primaryIndexer;
306 private RecipeTraceInfo secondaryIndexer;
307 private JoinRecipe naturalJoinRecipe;
308 private List<PVariable> naturalJoinVariablesTuple;
309
310 /**
311 * @pre enforceVariableCoincidences() has been called on both sides.
312 */
313 public JoinHelper(SubPlan planToCompile, PlanningTrace primaryCompiled, PlanningTrace callTrace) {
314 super();
315
316 Set<PVariable> primaryVariables = new LinkedHashSet<PVariable>(primaryCompiled.getVariablesTuple());
317 Set<PVariable> secondaryVariables = new LinkedHashSet<PVariable>(callTrace.getVariablesTuple());
318 int oldNodes = 0;
319 Set<Integer> introducingSecondaryIndices = new TreeSet<Integer>();
320 for (PVariable var : secondaryVariables) {
321 if (primaryVariables.contains(var))
322 oldNodes++;
323 else
324 introducingSecondaryIndices.add(callTrace.getPosMapping().get(var));
325 }
326 List<Integer> primaryIndices = new ArrayList<Integer>(oldNodes);
327 List<Integer> secondaryIndices = new ArrayList<Integer>(oldNodes);
328 for (PVariable var : secondaryVariables) {
329 if (primaryVariables.contains(var)) {
330 primaryIndices.add(primaryCompiled.getPosMapping().get(var));
331 secondaryIndices.add(callTrace.getPosMapping().get(var));
332 }
333 }
334 Collection<Integer> complementerIndices = introducingSecondaryIndices;
335
336 primaryMask = TupleMask.fromSelectedIndices(primaryCompiled.getVariablesTuple().size(), primaryIndices);
337 secondaryMask = TupleMask.fromSelectedIndices(callTrace.getVariablesTuple().size(), secondaryIndices);
338 complementerMask = TupleMask.fromSelectedIndices(callTrace.getVariablesTuple().size(), complementerIndices);
339
340 primaryIndexer = makeIndexerTrace(planToCompile, primaryCompiled, primaryMask);
341 secondaryIndexer = makeIndexerTrace(planToCompile, callTrace, secondaryMask);
342
343 naturalJoinRecipe = FACTORY.createJoinRecipe();
344 naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe());
345 naturalJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe());
346 naturalJoinRecipe.setRightParentComplementaryMask(CompilerHelper.toRecipeMask(complementerMask));
347
348 naturalJoinVariablesTuple = new ArrayList<PVariable>(primaryCompiled.getVariablesTuple());
349 for (int complementerIndex : complementerMask.indices)
350 naturalJoinVariablesTuple.add(callTrace.getVariablesTuple().get(complementerIndex));
351 }
352
353 public TupleMask getPrimaryMask() {
354 return primaryMask;
355 }
356
357 public TupleMask getSecondaryMask() {
358 return secondaryMask;
359 }
360
361 public TupleMask getComplementerMask() {
362 return complementerMask;
363 }
364
365 public RecipeTraceInfo getPrimaryIndexer() {
366 return primaryIndexer;
367 }
368
369 public RecipeTraceInfo getSecondaryIndexer() {
370 return secondaryIndexer;
371 }
372
373 public JoinRecipe getNaturalJoinRecipe() {
374 return naturalJoinRecipe;
375 }
376
377 public List<PVariable> getNaturalJoinVariablesTuple() {
378 return naturalJoinVariablesTuple;
379 }
380
381 }
382
383 /**
384 * @since 1.4
385 */
386 public static Mask toRecipeMask(TupleMask mask) {
387 return RecipesHelper.mask(mask.sourceWidth, mask.indices);
388 }
389
390}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.construction.plancompiler;
10
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.List;
14import java.util.Map;
15import java.util.stream.Collectors;
16
17import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
18import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
19import tools.refinery.viatra.runtime.matchers.psystem.PBody;
20import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
21import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
22import tools.refinery.viatra.runtime.rete.recipes.ProductionRecipe;
23import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
24import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery;
25import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
26
27/**
28 * In a recursive query structure, query composition references can be cut off so that the remaining structure is DAG.
29 * {@link RecursionCutoffPoint} represents one such cut off query composition.
30 * When the compilation of the recursive query finishes and the compiled form becomes available,
31 * the {@link RecursionCutoffPoint} has to be signaled to update parent traces and recipes of the recursive call.
32 *
33 * @author Bergmann Gabor
34 * @noreference This class is not intended to be referenced by clients
35 *
36 */
37public class RecursionCutoffPoint {
38 final Map<PBody, RecipeTraceInfo> futureTraceMap;
39 final CompiledQuery compiledQuery;
40 final ProductionRecipe recipe;
41 final QueryEvaluationHint hint;
42
43 public RecursionCutoffPoint(PQuery query, QueryEvaluationHint hint, IQueryMetaContext context, boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) {
44 super();
45 this.hint = hint;
46 this.futureTraceMap = new HashMap<>(); // IMPORTANT: the identity of futureTraceMap.values() will not change
47 this.compiledQuery = CompilerHelper.makeQueryTrace(query, futureTraceMap, Collections.<ReteNodeRecipe>emptySet(), hint, context, deleteAndRederiveEvaluation, timelyEvaluation);
48 this.recipe = (ProductionRecipe)compiledQuery.getRecipe();
49 if (!compiledQuery.getParentRecipeTraces().isEmpty()) {
50 throw new IllegalArgumentException(String.format("Recursion cut-off point of query %s has trace parents: %s",
51 compiledQuery.getQuery(),
52 prettyPrintParentRecipeTraces(compiledQuery.getParentRecipeTraces())));
53 }
54 if (!recipe.getParents().isEmpty()) {
55 throw new IllegalArgumentException(String.format("Recursion cut-off point of query %s has recipe parents: %s",
56 compiledQuery.getQuery(),
57 prettyPrintParentRecipeTraces(compiledQuery.getParentRecipeTraces())));
58 }
59 }
60
61
62
63 private String prettyPrintParentRecipeTraces(List<RecipeTraceInfo> trace) {
64 return trace.stream().map(Object::toString).collect(Collectors.joining(", "));
65 }
66
67 /**
68 * Signals that compilation of the recursive query has terminated, culminating into the given compiled form.
69 * The query composition that has been cut off will be connected now.
70 */
71 public void mend(CompiledQuery finalCompiledForm) {
72 futureTraceMap.putAll(finalCompiledForm.getParentRecipeTracesPerBody());
73 recipe.getParents().addAll(((ProductionRecipe)finalCompiledForm.getRecipe()).getParents());
74 }
75
76 public CompiledQuery getCompiledQuery() {
77 return compiledQuery;
78 }
79
80 public ProductionRecipe getRecipe() {
81 return recipe;
82 }
83
84
85
86}
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..5df3a971
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java
@@ -0,0 +1,949 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.viatra.runtime.rete.construction.plancompiler;
11
12import org.apache.log4j.Logger;
13import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
14import tools.refinery.viatra.runtime.matchers.backend.CommonQueryHintOptions;
15import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
16import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
17import tools.refinery.viatra.runtime.matchers.context.IInputKey;
18import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
19import tools.refinery.viatra.runtime.matchers.context.IQueryCacheContext;
20import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
21import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy;
22import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
23import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper;
24import tools.refinery.viatra.runtime.matchers.planning.operations.*;
25import tools.refinery.viatra.runtime.matchers.psystem.*;
26import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
27import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
28import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*;
29import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*;
30import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter;
31import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
32import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility;
33import tools.refinery.viatra.runtime.matchers.psystem.rewriters.*;
34import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
35import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
36import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
37import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
38import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
39import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
40import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper.JoinHelper;
41import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper.PosetTriplet;
42import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
43import tools.refinery.viatra.runtime.rete.recipes.*;
44import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper;
45import tools.refinery.viatra.runtime.rete.traceability.*;
46import tools.refinery.viatra.runtime.rete.util.ReteHintOptions;
47
48import java.util.*;
49import java.util.Map.Entry;
50import java.util.function.BiFunction;
51import java.util.function.Function;
52
53/**
54 * Compiles queries and query plans into Rete recipes, traced by respectively a {@link CompiledQuery} or
55 * {@link CompiledSubPlan}.
56 *
57 * @author Bergmann Gabor
58 *
59 */
60public class ReteRecipeCompiler {
61
62 private final IQueryPlannerStrategy plannerStrategy;
63 private final IQueryMetaContext metaContext;
64 private final IQueryBackendHintProvider hintProvider;
65 private final PDisjunctionRewriter normalizer;
66 private final QueryAnalyzer queryAnalyzer;
67 private final Logger logger;
68
69 /**
70 * @since 2.2
71 */
72 protected final boolean deleteAndRederiveEvaluation;
73 /**
74 * @since 2.4
75 */
76 protected final TimelyConfiguration timelyEvaluation;
77
78 /**
79 * @since 1.5
80 */
81 public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext,
82 IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer) {
83 this(plannerStrategy, logger, metaContext, queryCacheContext, hintProvider, queryAnalyzer, false, null);
84 }
85
86 /**
87 * @since 2.4
88 */
89 public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext,
90 IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer,
91 boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) {
92 super();
93 this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation;
94 this.timelyEvaluation = timelyEvaluation;
95 this.plannerStrategy = plannerStrategy;
96 this.logger = logger;
97 this.metaContext = metaContext;
98 this.queryAnalyzer = queryAnalyzer;
99 this.normalizer = new PDisjunctionRewriterCacher(new SurrogateQueryRewriter(),
100 new PBodyNormalizer(metaContext) {
101
102 @Override
103 protected boolean shouldExpandWeakenedAlternatives(PQuery query) {
104 QueryEvaluationHint hint = ReteRecipeCompiler.this.hintProvider.getQueryEvaluationHint(query);
105 Boolean expandWeakenedAlternativeConstraints = ReteHintOptions.expandWeakenedAlternativeConstraints
106 .getValueOrDefault(hint);
107 return expandWeakenedAlternativeConstraints;
108 }
109
110 });
111 this.hintProvider = hintProvider;
112 }
113
114 static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE;
115
116 // INTERNALLY CACHED
117 private Map<PBody, SubPlan> plannerCache = new HashMap<PBody, SubPlan>();
118 private Set<PBody> planningInProgress = new HashSet<PBody>();
119
120 private Map<PQuery, CompiledQuery> queryCompilerCache = new HashMap<PQuery, CompiledQuery>();
121 private Set<PQuery> compilationInProgress = new HashSet<PQuery>();
122 private IMultiLookup<PQuery, RecursionCutoffPoint> recursionCutoffPoints = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class);
123 private Map<SubPlan, CompiledSubPlan> subPlanCompilerCache = new HashMap<SubPlan, CompiledSubPlan>();
124 private Map<ReteNodeRecipe, SubPlan> compilerBackTrace = new HashMap<ReteNodeRecipe, SubPlan>();
125
126 /**
127 * Clears internal state
128 */
129 public void reset() {
130 plannerCache.clear();
131 planningInProgress.clear();
132 queryCompilerCache.clear();
133 subPlanCompilerCache.clear();
134 compilerBackTrace.clear();
135 }
136
137 /**
138 * Returns a {@link CompiledQuery} compiled from a query
139 * @throws ViatraQueryRuntimeException
140 */
141 public CompiledQuery getCompiledForm(PQuery query) {
142 CompiledQuery compiled = queryCompilerCache.get(query);
143 if (compiled == null) {
144
145 IRewriterTraceCollector traceCollector = CommonQueryHintOptions.normalizationTraceCollector
146 .getValueOrDefault(hintProvider.getQueryEvaluationHint(query));
147 if (traceCollector != null) {
148 traceCollector.addTrace(query, query);
149 }
150
151 boolean reentrant = !compilationInProgress.add(query);
152 if (reentrant) { // oops, recursion into body in progress
153 RecursionCutoffPoint cutoffPoint = new RecursionCutoffPoint(query, getHints(query), metaContext,
154 deleteAndRederiveEvaluation, timelyEvaluation);
155 recursionCutoffPoints.addPair(query, cutoffPoint);
156 return cutoffPoint.getCompiledQuery();
157 } else { // not reentrant, therefore no recursion, do the compilation
158 try {
159 compiled = compileProduction(query);
160 queryCompilerCache.put(query, compiled);
161 // backTrace.put(compiled.getRecipe(), plan);
162
163 // if this was a recursive query, mend all points where recursion was cut off
164 for (RecursionCutoffPoint cutoffPoint : recursionCutoffPoints.lookupOrEmpty(query)) {
165 cutoffPoint.mend(compiled);
166 }
167 } finally {
168 compilationInProgress.remove(query);
169 }
170 }
171 }
172 return compiled;
173 }
174
175 /**
176 * Returns a {@link CompiledSubPlan} compiled from a query plan
177 * @throws ViatraQueryRuntimeException
178 */
179 public CompiledSubPlan getCompiledForm(SubPlan plan) {
180 CompiledSubPlan compiled = subPlanCompilerCache.get(plan);
181 if (compiled == null) {
182 compiled = doCompileDispatch(plan);
183 subPlanCompilerCache.put(plan, compiled);
184 compilerBackTrace.put(compiled.getRecipe(), plan);
185 }
186 return compiled;
187 }
188
189 /**
190 * @throws ViatraQueryRuntimeException
191 */
192 public SubPlan getPlan(PBody pBody) {
193 // if the query is not marked as being compiled, initiate compilation
194 // (this is useful in case of recursion if getPlan() is the entry point)
195 PQuery pQuery = pBody.getPattern();
196 if (!compilationInProgress.contains(pQuery))
197 getCompiledForm(pQuery);
198
199 // Is the plan already cached?
200 SubPlan plan = plannerCache.get(pBody);
201 if (plan == null) {
202 boolean reentrant = !planningInProgress.add(pBody);
203 if (reentrant) { // oops, recursion into body in progress
204 throw new IllegalArgumentException(
205 "Planning-level recursion unsupported: " + pBody.getPattern().getFullyQualifiedName());
206 } else { // not reentrant, therefore no recursion, do the planning
207 try {
208 plan = plannerStrategy.plan(pBody, logger, metaContext);
209 plannerCache.put(pBody, plan);
210 } finally {
211 planningInProgress.remove(pBody);
212 }
213 }
214 }
215 return plan;
216 }
217
218 private CompiledQuery compileProduction(PQuery query) {
219 Collection<SubPlan> bodyPlans = new ArrayList<SubPlan>();
220 normalizer.setTraceCollector(CommonQueryHintOptions.normalizationTraceCollector
221 .getValueOrDefault(hintProvider.getQueryEvaluationHint(query)));
222 for (PBody pBody : normalizer.rewrite(query).getBodies()) {
223 SubPlan bodyPlan = getPlan(pBody);
224 bodyPlans.add(bodyPlan);
225 }
226 return doCompileProduction(query, bodyPlans);
227 }
228
229 private CompiledQuery doCompileProduction(PQuery query, Collection<SubPlan> bodies) {
230 // TODO skip production node if there is just one body and no projection needed?
231 Map<PBody, RecipeTraceInfo> bodyFinalTraces = new HashMap<PBody, RecipeTraceInfo>();
232 Collection<ReteNodeRecipe> bodyFinalRecipes = new HashSet<ReteNodeRecipe>();
233
234 for (SubPlan bodyFinalPlan : bodies) {
235 // skip over any projections at the end
236 bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan);
237
238 // TODO checkAndTrimEqualVariables may introduce superfluous trim,
239 // but whatever (no uniqueness enforcer needed)
240
241 // compile body
242 final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan);
243
244 // project to parameter list
245 RecipeTraceInfo finalTrace = projectBodyFinalToParameters(compiledBody, false);
246
247 bodyFinalTraces.put(bodyFinalPlan.getBody(), finalTrace);
248 bodyFinalRecipes.add(finalTrace.getRecipe());
249 }
250
251 CompiledQuery compiled = CompilerHelper.makeQueryTrace(query, bodyFinalTraces, bodyFinalRecipes,
252 getHints(query), metaContext, deleteAndRederiveEvaluation, timelyEvaluation);
253
254 return compiled;
255 }
256
257 private CompiledSubPlan doCompileDispatch(SubPlan plan) {
258 final POperation operation = plan.getOperation();
259 if (operation instanceof PEnumerate) {
260 return doCompileEnumerate(((PEnumerate) operation).getEnumerablePConstraint(), plan);
261 } else if (operation instanceof PApply) {
262 final PConstraint pConstraint = ((PApply) operation).getPConstraint();
263 if (pConstraint instanceof EnumerablePConstraint) {
264 CompiledSubPlan primaryParent = getCompiledForm(plan.getParentPlans().get(0));
265 PlanningTrace secondaryParent = doEnumerateDispatch(plan, (EnumerablePConstraint) pConstraint);
266 return compileToNaturalJoin(plan, primaryParent, secondaryParent);
267 } else if (pConstraint instanceof DeferredPConstraint) {
268 return doDeferredDispatch((DeferredPConstraint) pConstraint, plan);
269 } else {
270 throw new IllegalArgumentException("Unsupported PConstraint in query plan: " + plan.toShortString());
271 }
272 } else if (operation instanceof PJoin) {
273 return doCompileJoin((PJoin) operation, plan);
274 } else if (operation instanceof PProject) {
275 return doCompileProject((PProject) operation, plan);
276 } else if (operation instanceof PStart) {
277 return doCompileStart((PStart) operation, plan);
278 } else {
279 throw new IllegalArgumentException("Unsupported POperation in query plan: " + plan.toShortString());
280 }
281 }
282
283 private CompiledSubPlan doDeferredDispatch(DeferredPConstraint constraint, SubPlan plan) {
284 final SubPlan parentPlan = plan.getParentPlans().get(0);
285 final CompiledSubPlan parentCompiled = getCompiledForm(parentPlan);
286 if (constraint instanceof Equality) {
287 return compileDeferred((Equality) constraint, plan, parentPlan, parentCompiled);
288 } else if (constraint instanceof ExportedParameter) {
289 return compileDeferred((ExportedParameter) constraint, plan, parentPlan, parentCompiled);
290 } else if (constraint instanceof Inequality) {
291 return compileDeferred((Inequality) constraint, plan, parentPlan, parentCompiled);
292 } else if (constraint instanceof NegativePatternCall) {
293 return compileDeferred((NegativePatternCall) constraint, plan, parentPlan, parentCompiled);
294 } else if (constraint instanceof PatternMatchCounter) {
295 return compileDeferred((PatternMatchCounter) constraint, plan, parentPlan, parentCompiled);
296 } else if (constraint instanceof AggregatorConstraint) {
297 return compileDeferred((AggregatorConstraint) constraint, plan, parentPlan, parentCompiled);
298 } else if (constraint instanceof ExpressionEvaluation) {
299 return compileDeferred((ExpressionEvaluation) constraint, plan, parentPlan, parentCompiled);
300 } else if (constraint instanceof TypeFilterConstraint) {
301 return compileDeferred((TypeFilterConstraint) constraint, plan, parentPlan, parentCompiled);
302 }
303 throw new UnsupportedOperationException("Unknown deferred constraint " + constraint);
304 }
305
306 private CompiledSubPlan compileDeferred(Equality constraint, SubPlan plan, SubPlan parentPlan,
307 CompiledSubPlan parentCompiled) {
308 if (constraint.isMoot())
309 return parentCompiled.cloneFor(plan);
310
311 Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho());
312 Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom());
313
314 if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) {
315 Integer indexLower = Math.min(index1, index2);
316 Integer indexHigher = Math.max(index1, index2);
317
318 EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe();
319 equalityFilterRecipe.setParent(parentCompiled.getRecipe());
320 equalityFilterRecipe.getIndices().add(indexLower);
321 equalityFilterRecipe.getIndices().add(indexHigher);
322
323 return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), equalityFilterRecipe, parentCompiled);
324 } else {
325 throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s",
326 plan.toShortString(), parentCompiled.toString()));
327 }
328 }
329
330 /**
331 * Precondition: constantTrace must map to a ConstantRecipe, and all of its variables must be contained in
332 * toFilterTrace.
333 */
334 private CompiledSubPlan compileConstantFiltering(SubPlan plan, PlanningTrace toFilterTrace,
335 ConstantRecipe constantRecipe, List<PVariable> filteredVariables) {
336 PlanningTrace resultTrace = toFilterTrace;
337
338 int constantVariablesSize = filteredVariables.size();
339 for (int i = 0; i < constantVariablesSize; ++i) {
340 Object constantValue = constantRecipe.getConstantValues().get(i);
341 PVariable filteredVariable = filteredVariables.get(i);
342 int filteredColumn = resultTrace.getVariablesTuple().indexOf(filteredVariable);
343
344 DiscriminatorDispatcherRecipe dispatcherRecipe = FACTORY.createDiscriminatorDispatcherRecipe();
345 dispatcherRecipe.setDiscriminationColumnIndex(filteredColumn);
346 dispatcherRecipe.setParent(resultTrace.getRecipe());
347
348 PlanningTrace dispatcherTrace = new PlanningTrace(plan, resultTrace.getVariablesTuple(), dispatcherRecipe,
349 resultTrace);
350
351 DiscriminatorBucketRecipe bucketRecipe = FACTORY.createDiscriminatorBucketRecipe();
352 bucketRecipe.setBucketKey(constantValue);
353 bucketRecipe.setParent(dispatcherRecipe);
354
355 PlanningTrace bucketTrace = new PlanningTrace(plan, dispatcherTrace.getVariablesTuple(), bucketRecipe,
356 dispatcherTrace);
357
358 resultTrace = bucketTrace;
359 }
360
361 return resultTrace.cloneFor(plan);
362 }
363
364 private CompiledSubPlan compileDeferred(ExportedParameter constraint, SubPlan plan, SubPlan parentPlan,
365 CompiledSubPlan parentCompiled) {
366 return parentCompiled.cloneFor(plan);
367 }
368
369 private CompiledSubPlan compileDeferred(Inequality constraint, SubPlan plan, SubPlan parentPlan,
370 CompiledSubPlan parentCompiled) {
371 if (constraint.isEliminable())
372 return parentCompiled.cloneFor(plan);
373
374 Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho());
375 Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom());
376
377 if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) {
378 Integer indexLower = Math.min(index1, index2);
379 Integer indexHigher = Math.max(index1, index2);
380
381 InequalityFilterRecipe inequalityFilterRecipe = FACTORY.createInequalityFilterRecipe();
382 inequalityFilterRecipe.setParent(parentCompiled.getRecipe());
383 inequalityFilterRecipe.setSubject(indexLower);
384 inequalityFilterRecipe.getInequals().add(indexHigher);
385
386 return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), inequalityFilterRecipe,
387 parentCompiled);
388 } else {
389 throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s",
390 plan.toShortString(), parentCompiled.toString()));
391 }
392 }
393
394 private CompiledSubPlan compileDeferred(TypeFilterConstraint constraint, SubPlan plan, SubPlan parentPlan,
395 CompiledSubPlan parentCompiled) {
396 final IInputKey inputKey = constraint.getInputKey();
397 if (!metaContext.isStateless(inputKey))
398 throw new UnsupportedOperationException(
399 "Non-enumerable input keys are currently supported in Rete only if they are stateless, unlike "
400 + inputKey);
401
402 final Tuple constraintVariables = constraint.getVariablesTuple();
403 final List<PVariable> parentVariables = parentCompiled.getVariablesTuple();
404
405 Mask mask; // select elements of the tuple to check against extensional relation
406 if (Tuples.flatTupleOf(parentVariables.toArray()).equals(constraintVariables)) {
407 mask = null; // lucky case, parent signature equals that of input key
408 } else {
409 List<PVariable> variables = new ArrayList<PVariable>();
410 for (Object variable : constraintVariables.getElements()) {
411 variables.add((PVariable) variable);
412 }
413 mask = CompilerHelper.makeProjectionMask(parentCompiled, variables);
414 }
415 InputFilterRecipe inputFilterRecipe = RecipesHelper.inputFilterRecipe(parentCompiled.getRecipe(), inputKey,
416 inputKey.getStringID(), mask);
417 return new CompiledSubPlan(plan, parentVariables, inputFilterRecipe, parentCompiled);
418 }
419
420 private CompiledSubPlan compileDeferred(NegativePatternCall constraint, SubPlan plan, SubPlan parentPlan,
421 CompiledSubPlan parentCompiled) {
422 final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan,
423 constraint.getActualParametersTuple());
424
425 JoinHelper joinHelper = new JoinHelper(plan, parentCompiled, callTrace);
426 final RecipeTraceInfo primaryIndexer = joinHelper.getPrimaryIndexer();
427 final RecipeTraceInfo secondaryIndexer = joinHelper.getSecondaryIndexer();
428
429 AntiJoinRecipe antiJoinRecipe = FACTORY.createAntiJoinRecipe();
430 antiJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe());
431 antiJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe());
432
433 return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), antiJoinRecipe, primaryIndexer,
434 secondaryIndexer);
435 }
436
437 private CompiledSubPlan compileDeferred(PatternMatchCounter constraint, SubPlan plan, SubPlan parentPlan,
438 CompiledSubPlan parentCompiled) {
439 final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan,
440 constraint.getActualParametersTuple());
441
442 // hack: use some mask computations (+ the indexers) from a fake natural join against the called query
443 JoinHelper fakeJoinHelper = new JoinHelper(plan, parentCompiled, callTrace);
444 final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer();
445 final RecipeTraceInfo callProjectionIndexer = fakeJoinHelper.getSecondaryIndexer();
446
447 final List<PVariable> sideVariablesTuple = new ArrayList<PVariable>(
448 fakeJoinHelper.getSecondaryMask().transform(callTrace.getVariablesTuple()));
449 /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable());
450
451 CountAggregatorRecipe aggregatorRecipe = FACTORY.createCountAggregatorRecipe();
452 aggregatorRecipe.setParent((ProjectionIndexerRecipe) callProjectionIndexer.getRecipe());
453 PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe,
454 callProjectionIndexer);
455
456 IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe();
457 aggregatorIndexerRecipe.setParent(aggregatorRecipe);
458 // aggregatorIndexerRecipe.setMask(RecipesHelper.mask(
459 // sideVariablesTuple.size(),
460 // //use same indices as in the projection indexer
461 // // EVEN if result variable already visible in left parent
462 // fakeJoinHelper.getSecondaryMask().indices
463 // ));
464
465 int aggregatorWidth = sideVariablesTuple.size();
466 int aggregateResultIndex = aggregatorWidth - 1;
467
468 aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit(
469 // aggregate according all but the last index
470 aggregateResultIndex, aggregatorWidth)));
471 PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe,
472 aggregatorTrace);
473
474 JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe();
475 naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe());
476 naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe);
477 naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth,
478 // extend with last element only - the computation value
479 aggregateResultIndex));
480
481 // what if the new variable already has a value?
482 // even if already known, we add the new result variable, so that it can be filtered at the end
483 // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable());
484
485 final List<PVariable> aggregatedVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple());
486 aggregatedVariablesTuple.add(constraint.getResultVariable());
487
488 PlanningTrace joinTrace = new PlanningTrace(plan, aggregatedVariablesTuple, naturalJoinRecipe, primaryIndexer,
489 aggregatorIndexerTrace);
490
491 return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan);
492 // if (!alreadyKnown) {
493 // return joinTrace.cloneFor(plan);
494 // } else {
495 // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple());
496 // }
497 }
498
499 private CompiledSubPlan compileDeferred(AggregatorConstraint constraint, SubPlan plan, SubPlan parentPlan,
500 CompiledSubPlan parentCompiled) {
501 final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan,
502 constraint.getActualParametersTuple());
503
504 // hack: use some mask computations (+ the indexers) from a fake natural join against the called query
505 JoinHelper fakeJoinHelper = new JoinHelper(plan, parentCompiled, callTrace);
506 final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer();
507 TupleMask callGroupMask = fakeJoinHelper.getSecondaryMask();
508
509 final List<PVariable> sideVariablesTuple = new ArrayList<PVariable>(
510 callGroupMask.transform(callTrace.getVariablesTuple()));
511 /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable());
512
513 IMultisetAggregationOperator<?, ?, ?> operator = constraint.getAggregator().getOperator();
514
515 SingleColumnAggregatorRecipe columnAggregatorRecipe = FACTORY.createSingleColumnAggregatorRecipe();
516 columnAggregatorRecipe.setParent(callTrace.getRecipe());
517 columnAggregatorRecipe.setMultisetAggregationOperator(operator);
518
519 int columnIndex = constraint.getAggregatedColumn();
520 IPosetComparator posetComparator = null;
521 Mask groupMask = CompilerHelper.toRecipeMask(callGroupMask);
522
523 // temporary solution to support the deprecated option for now
524 final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(plan));
525
526 columnAggregatorRecipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep);
527 if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) {
528 List<PParameter> parameters = constraint.getReferredQuery().getParameters();
529 IInputKey key = parameters.get(columnIndex).getDeclaredUnaryType();
530 if (key != null && metaContext.isPosetKey(key)) {
531 posetComparator = metaContext.getPosetComparator(Collections.singleton(key));
532 }
533 }
534
535 if (posetComparator == null) {
536 columnAggregatorRecipe.setGroupByMask(groupMask);
537 columnAggregatorRecipe.setAggregableIndex(columnIndex);
538 } else {
539 MonotonicityInfo monotonicityInfo = FACTORY.createMonotonicityInfo();
540 monotonicityInfo.setCoreMask(groupMask);
541 monotonicityInfo.setPosetMask(CompilerHelper.toRecipeMask(
542 TupleMask.selectSingle(columnIndex, constraint.getActualParametersTuple().getSize())));
543 monotonicityInfo.setPosetComparator(posetComparator);
544 columnAggregatorRecipe.setOptionalMonotonicityInfo(monotonicityInfo);
545 }
546
547 ReteNodeRecipe aggregatorRecipe = columnAggregatorRecipe;
548 PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe, callTrace);
549
550 IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe();
551 aggregatorIndexerRecipe.setParent(aggregatorRecipe);
552
553 int aggregatorWidth = sideVariablesTuple.size();
554 int aggregateResultIndex = aggregatorWidth - 1;
555
556 aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit(
557 // aggregate according all but the last index
558 aggregateResultIndex, aggregatorWidth)));
559 PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe,
560 aggregatorTrace);
561
562 JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe();
563 naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe());
564 naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe);
565 naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth,
566 // extend with last element only - the computation value
567 aggregateResultIndex));
568
569 // what if the new variable already has a value?
570 // even if already known, we add the new result variable, so that it can be filtered at the end
571 // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable());
572
573 final List<PVariable> finalVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple());
574 finalVariablesTuple.add(constraint.getResultVariable());
575
576 PlanningTrace joinTrace = new PlanningTrace(plan, finalVariablesTuple, naturalJoinRecipe, primaryIndexer,
577 aggregatorIndexerTrace);
578
579 return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan);
580 // if (!alreadyKnown) {
581 // return joinTrace.cloneFor(plan);
582 // } else {
583 // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple());
584 // }
585 }
586
587 private CompiledSubPlan compileDeferred(ExpressionEvaluation constraint, SubPlan plan, SubPlan parentPlan,
588 CompiledSubPlan parentCompiled) {
589 Map<String, Integer> tupleNameMap = new HashMap<String, Integer>();
590 for (String name : constraint.getEvaluator().getInputParameterNames()) {
591 Map<? extends Object, Integer> index = parentCompiled.getPosMapping();
592 PVariable variable = constraint.getPSystem().getVariableByNameChecked(name);
593 Integer position = index.get(variable);
594 tupleNameMap.put(name, position);
595 }
596
597 final PVariable outputVariable = constraint.getOutputVariable();
598 final boolean booleanCheck = outputVariable == null;
599
600 // TODO determine whether expression is costly
601 boolean cacheOutput = ReteHintOptions.cacheOutputOfEvaluatorsByDefault.getValueOrDefault(getHints(plan));
602 // for (PAnnotation pAnnotation :
603 // plan.getBody().getPattern().getAnnotationsByName(EXPRESSION_EVALUATION_ANNOTATION"")) {
604 // for (Object value : pAnnotation.getAllValues("expensive")) {
605 // if (value instanceof Boolean)
606 // cacheOutput = (boolean) value;
607 // }
608 // }
609
610 ExpressionEnforcerRecipe enforcerRecipe = booleanCheck ? FACTORY.createCheckRecipe()
611 : FACTORY.createEvalRecipe();
612 enforcerRecipe.setParent(parentCompiled.getRecipe());
613 enforcerRecipe.setExpression(RecipesHelper.expressionDefinition(constraint.getEvaluator()));
614 enforcerRecipe.setCacheOutput(cacheOutput);
615 if (enforcerRecipe instanceof EvalRecipe) {
616 ((EvalRecipe) enforcerRecipe).setUnwinding(constraint.isUnwinding());
617 }
618 for (Entry<String, Integer> entry : tupleNameMap.entrySet()) {
619 enforcerRecipe.getMappedIndices().put(entry.getKey(), entry.getValue());
620 }
621
622 final List<PVariable> enforcerVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple());
623 if (!booleanCheck)
624 enforcerVariablesTuple.add(outputVariable);
625 PlanningTrace enforcerTrace = new PlanningTrace(plan, enforcerVariablesTuple, enforcerRecipe, parentCompiled);
626
627 return CompilerHelper.checkAndTrimEqualVariables(plan, enforcerTrace).cloneFor(plan);
628 }
629
630 private CompiledSubPlan doCompileJoin(PJoin operation, SubPlan plan) {
631 final List<CompiledSubPlan> compiledParents = getCompiledFormOfParents(plan);
632 final CompiledSubPlan leftCompiled = compiledParents.get(0);
633 final CompiledSubPlan rightCompiled = compiledParents.get(1);
634
635 return compileToNaturalJoin(plan, leftCompiled, rightCompiled);
636 }
637
638 private CompiledSubPlan compileToNaturalJoin(SubPlan plan, final PlanningTrace leftCompiled,
639 final PlanningTrace rightCompiled) {
640 // CHECK IF SPECIAL CASE
641
642 // Is constant filtering applicable?
643 if (ReteHintOptions.useDiscriminatorDispatchersForConstantFiltering.getValueOrDefault(getHints(plan))) {
644 if (leftCompiled.getRecipe() instanceof ConstantRecipe
645 && rightCompiled.getVariablesTuple().containsAll(leftCompiled.getVariablesTuple())) {
646 return compileConstantFiltering(plan, rightCompiled, (ConstantRecipe) leftCompiled.getRecipe(),
647 leftCompiled.getVariablesTuple());
648 }
649 if (rightCompiled.getRecipe() instanceof ConstantRecipe
650 && leftCompiled.getVariablesTuple().containsAll(rightCompiled.getVariablesTuple())) {
651 return compileConstantFiltering(plan, leftCompiled, (ConstantRecipe) rightCompiled.getRecipe(),
652 rightCompiled.getVariablesTuple());
653 }
654 }
655
656 // ELSE: ACTUAL JOIN
657 JoinHelper joinHelper = new JoinHelper(plan, leftCompiled, rightCompiled);
658 return new CompiledSubPlan(plan, joinHelper.getNaturalJoinVariablesTuple(), joinHelper.getNaturalJoinRecipe(),
659 joinHelper.getPrimaryIndexer(), joinHelper.getSecondaryIndexer());
660 }
661
662 private CompiledSubPlan doCompileProject(PProject operation, SubPlan plan) {
663 final List<CompiledSubPlan> compiledParents = getCompiledFormOfParents(plan);
664 final CompiledSubPlan compiledParent = compiledParents.get(0);
665
666 List<PVariable> projectedVariables = new ArrayList<PVariable>(operation.getToVariables());
667 // Determinizing projection: try to keep original order (hopefully facilitates node reuse)
668 Map<PVariable, Integer> parentPosMapping = compiledParent.getPosMapping();
669 Collections.sort(projectedVariables, Comparator.comparing(parentPosMapping::get));
670
671 return doProjectPlan(compiledParent, projectedVariables, true,
672 parentTrace -> parentTrace.cloneFor(plan),
673 (recipe, parentTrace) -> new PlanningTrace(plan, projectedVariables, recipe, parentTrace),
674 (recipe, parentTrace) -> new CompiledSubPlan(plan, projectedVariables, recipe, parentTrace)
675 );
676 }
677
678 /**
679 * Projects a subplan onto the specified variable tuple
680 * @param compiledParentPlan the compiled form of the subplan
681 * @param targetVariables list of variables to project to
682 * @param enforceUniqueness whether distinctness shall be enforced after the projection.
683 * Specify false only if directly connecting to a production node.
684 * @param reinterpretTraceFactory constructs a reinterpreted trace that simply relabels the compiled parent plan, in case it is sufficient
685 * @param intermediateTraceFactory constructs a recipe trace for an intermediate node, given the recipe of the node and its parent trace
686 * @param finalTraceFactory constructs a recipe trace for the final resulting node, given the recipe of the node and its parent trace
687 * @since 2.1
688 */
689 <ResultTrace extends RecipeTraceInfo> ResultTrace doProjectPlan(
690 final CompiledSubPlan compiledParentPlan,
691 final List<PVariable> targetVariables,
692 boolean enforceUniqueness,
693 Function<CompiledSubPlan, ResultTrace> reinterpretTraceFactory,
694 BiFunction<ReteNodeRecipe, RecipeTraceInfo, RecipeTraceInfo> intermediateTraceFactory,
695 BiFunction<ReteNodeRecipe, RecipeTraceInfo, ResultTrace> finalTraceFactory)
696 {
697 if (targetVariables.equals(compiledParentPlan.getVariablesTuple())) // no projection needed
698 return reinterpretTraceFactory.apply(compiledParentPlan);
699
700 // otherwise, we need at least a trimmer
701 TrimmerRecipe trimmerRecipe = CompilerHelper.makeTrimmerRecipe(compiledParentPlan, targetVariables);
702
703 // do we need to eliminate duplicates?
704 SubPlan parentPlan = compiledParentPlan.getSubPlan();
705 if (!enforceUniqueness || BuildHelper.areAllVariablesDetermined(
706 parentPlan,
707 targetVariables,
708 queryAnalyzer,
709 true))
710 {
711 // if uniqueness enforcess is unwanted or unneeeded, skip it
712 return finalTraceFactory.apply(trimmerRecipe, compiledParentPlan);
713 } else {
714 // add a uniqueness enforcer
715 UniquenessEnforcerRecipe recipe = FACTORY.createUniquenessEnforcerRecipe();
716 recipe.getParents().add(trimmerRecipe);
717
718 // temporary solution to support the deprecated option for now
719 final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(parentPlan));
720
721 recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep);
722 if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) {
723 PosetTriplet triplet = CompilerHelper.computePosetInfo(targetVariables, parentPlan.getBody(), metaContext);
724
725 if (triplet.comparator != null) {
726 MonotonicityInfo info = FACTORY.createMonotonicityInfo();
727 info.setCoreMask(triplet.coreMask);
728 info.setPosetMask(triplet.posetMask);
729 info.setPosetComparator(triplet.comparator);
730 recipe.setOptionalMonotonicityInfo(info);
731 }
732 }
733
734 RecipeTraceInfo trimmerTrace = intermediateTraceFactory.apply(trimmerRecipe, compiledParentPlan);
735 return finalTraceFactory.apply(recipe, trimmerTrace);
736 }
737 }
738
739 /**
740 * Projects the final compiled form of a PBody onto the parameter tuple
741 * @param compiledBody the compiled form of the body, with all constraints enforced, not yet projected to query parameters
742 * @param enforceUniqueness whether distinctness shall be enforced after the projection.
743 * Specify false only if directly connecting to a production node.
744 * @since 2.1
745 */
746 RecipeTraceInfo projectBodyFinalToParameters(
747 final CompiledSubPlan compiledBody,
748 boolean enforceUniqueness)
749 {
750 final PBody body = compiledBody.getSubPlan().getBody();
751 final List<PVariable> parameterList = body.getSymbolicParameterVariables();
752
753 return doProjectPlan(compiledBody, parameterList, enforceUniqueness,
754 parentTrace -> parentTrace,
755 (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace),
756 (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace)
757 );
758 }
759
760 private CompiledSubPlan doCompileStart(PStart operation, SubPlan plan) {
761 if (!operation.getAPrioriVariables().isEmpty()) {
762 throw new IllegalArgumentException("Input variables unsupported by Rete: " + plan.toShortString());
763 }
764 final ConstantRecipe recipe = FACTORY.createConstantRecipe();
765 recipe.getConstantValues().clear();
766
767 return new CompiledSubPlan(plan, new ArrayList<PVariable>(), recipe);
768 }
769
770 private CompiledSubPlan doCompileEnumerate(EnumerablePConstraint constraint, SubPlan plan) {
771 final PlanningTrace trimmedTrace = doEnumerateAndDeduplicate(constraint, plan);
772
773 return trimmedTrace.cloneFor(plan);
774 }
775
776 private PlanningTrace doEnumerateAndDeduplicate(EnumerablePConstraint constraint, SubPlan plan) {
777 final PlanningTrace coreTrace = doEnumerateDispatch(plan, constraint);
778 final PlanningTrace trimmedTrace = CompilerHelper.checkAndTrimEqualVariables(plan, coreTrace);
779 return trimmedTrace;
780 }
781
782 private PlanningTrace doEnumerateDispatch(SubPlan plan, EnumerablePConstraint constraint) {
783 if (constraint instanceof RelationEvaluation) {
784 return compileEnumerable(plan, (RelationEvaluation) constraint);
785 } else if (constraint instanceof BinaryTransitiveClosure) {
786 return compileEnumerable(plan, (BinaryTransitiveClosure) constraint);
787 } else if (constraint instanceof BinaryReflexiveTransitiveClosure) {
788 return compileEnumerable(plan, (BinaryReflexiveTransitiveClosure) constraint);
789 } else if (constraint instanceof RepresentativeElectionConstraint) {
790 return compileEnumerable(plan, (RepresentativeElectionConstraint) constraint);
791 } else if (constraint instanceof ConstantValue) {
792 return compileEnumerable(plan, (ConstantValue) constraint);
793 } else if (constraint instanceof PositivePatternCall) {
794 return compileEnumerable(plan, (PositivePatternCall) constraint);
795 } else if (constraint instanceof TypeConstraint) {
796 return compileEnumerable(plan, (TypeConstraint) constraint);
797 }
798 throw new UnsupportedOperationException("Unknown enumerable constraint " + constraint);
799 }
800
801 private PlanningTrace compileEnumerable(SubPlan plan, BinaryReflexiveTransitiveClosure constraint) {
802 // TODO the implementation would perform better if an inequality check would be used after tcRecipe and
803 // uniqueness enforcer be replaced by a transparent node with multiple parents, but such a node is not available
804 // in recipe metamodel in VIATRA 2.0
805
806 // Find called query
807 final PQuery referredQuery = constraint.getSupplierKey();
808 final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple());
809
810 // Calculate irreflexive transitive closure
811 final TransitiveClosureRecipe tcRecipe = FACTORY.createTransitiveClosureRecipe();
812 tcRecipe.setParent(callTrace.getRecipe());
813 final PlanningTrace tcTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), tcRecipe, callTrace);
814
815 // Enumerate universe type
816 final IInputKey inputKey = constraint.getUniverseType();
817 final InputRecipe universeTypeRecipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity());
818 final PlanningTrace universeTypeTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(
819 Tuples.staticArityFlatTupleOf(constraint.getVariablesTuple().get(0))), universeTypeRecipe);
820
821 // Calculate reflexive access by duplicating universe type column
822 final TrimmerRecipe reflexiveRecipe = FACTORY.createTrimmerRecipe();
823 reflexiveRecipe.setMask(RecipesHelper.mask(1, 0, 0));
824 reflexiveRecipe.setParent(universeTypeRecipe);
825 final PlanningTrace reflexiveTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), reflexiveRecipe, universeTypeTrace);
826
827 // Finally, reduce duplicates after a join
828 final UniquenessEnforcerRecipe brtcRecipe = FACTORY.createUniquenessEnforcerRecipe();
829 brtcRecipe.getParents().add(tcRecipe);
830 brtcRecipe.getParents().add(reflexiveRecipe);
831
832 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), brtcRecipe, reflexiveTrace, tcTrace);
833 }
834
835 private PlanningTrace compileEnumerable(SubPlan plan, RepresentativeElectionConstraint constraint) {
836 var referredQuery = constraint.getSupplierKey();
837 var callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple());
838 var recipe = FACTORY.createRepresentativeElectionRecipe();
839 recipe.setParent(callTrace.getRecipe());
840 recipe.setConnectivity(constraint.getConnectivity());
841 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace);
842 }
843
844 private PlanningTrace compileEnumerable(SubPlan plan, BinaryTransitiveClosure constraint) {
845 final PQuery referredQuery = constraint.getSupplierKey();
846 final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple());
847
848 final TransitiveClosureRecipe recipe = FACTORY.createTransitiveClosureRecipe();
849 recipe.setParent(callTrace.getRecipe());
850
851 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace);
852 }
853
854 private PlanningTrace compileEnumerable(SubPlan plan, RelationEvaluation constraint) {
855 final List<ReteNodeRecipe> parentRecipes = new ArrayList<ReteNodeRecipe>();
856 final List<RecipeTraceInfo> parentTraceInfos = new ArrayList<RecipeTraceInfo>();
857 for (final PQuery inputQuery : constraint.getReferredQueries()) {
858 final CompiledQuery compiledQuery = getCompiledForm(inputQuery);
859 parentRecipes.add(compiledQuery.getRecipe());
860 parentTraceInfos.add(compiledQuery);
861 }
862 final RelationEvaluationRecipe recipe = FACTORY.createRelationEvaluationRecipe();
863 recipe.getParents().addAll(parentRecipes);
864 recipe.setEvaluator(RecipesHelper.expressionDefinition(constraint.getEvaluator()));
865 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, parentTraceInfos);
866 }
867
868 private PlanningTrace compileEnumerable(SubPlan plan, PositivePatternCall constraint) {
869 final PQuery referredQuery = constraint.getReferredQuery();
870 return referQuery(referredQuery, plan, constraint.getVariablesTuple());
871 }
872
873 private PlanningTrace compileEnumerable(SubPlan plan, TypeConstraint constraint) {
874 final IInputKey inputKey = constraint.getSupplierKey();
875 final InputRecipe recipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity());
876 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe);
877 }
878
879 private PlanningTrace compileEnumerable(SubPlan plan, ConstantValue constraint) {
880 final ConstantRecipe recipe = FACTORY.createConstantRecipe();
881 recipe.getConstantValues().add(constraint.getSupplierKey());
882 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe);
883 }
884
885 // TODO handle recursion
886 private PlanningTrace referQuery(PQuery query, SubPlan plan, Tuple actualParametersTuple) {
887 RecipeTraceInfo referredQueryTrace = originalTraceOfReferredQuery(query);
888 return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(actualParametersTuple),
889 referredQueryTrace.getRecipe(), referredQueryTrace.getParentRecipeTracesForCloning());
890 }
891
892 private RecipeTraceInfo originalTraceOfReferredQuery(PQuery query) {
893 // eliminate superfluous production node?
894 if (PVisibility.EMBEDDED == query.getVisibility()) { // currently inline patterns only
895 Set<PBody> rewrittenBodies = normalizer.rewrite(query).getBodies();
896 if (1 == rewrittenBodies.size()) { // non-disjunctive
897 // TODO in the future, check if non-recursive - (not currently permitted)
898
899 PBody pBody = rewrittenBodies.iterator().next();
900 SubPlan bodyFinalPlan = getPlan(pBody);
901
902 // skip over any projections at the end
903 bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan);
904
905 // TODO checkAndTrimEqualVariables may introduce superfluous trim,
906 // but whatever (no uniqueness enforcer needed)
907
908 // compile body
909 final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan);
910
911 // project to parameter list, add uniqueness enforcer if necessary
912 return projectBodyFinalToParameters(compiledBody, true /* ensure uniqueness, as no production node is used */);
913 }
914 }
915
916 // otherwise, regular reference to recipe realizing the query
917 return getCompiledForm(query);
918 }
919
920 protected List<CompiledSubPlan> getCompiledFormOfParents(SubPlan plan) {
921 List<CompiledSubPlan> results = new ArrayList<CompiledSubPlan>();
922 for (SubPlan parentPlan : plan.getParentPlans()) {
923 results.add(getCompiledForm(parentPlan));
924 }
925 return results;
926 }
927
928 /**
929 * Returns an unmodifiable view of currently cached compiled queries.
930 */
931 public Map<PQuery, CompiledQuery> getCachedCompiledQueries() {
932 return Collections.unmodifiableMap(queryCompilerCache);
933 }
934
935 /**
936 * Returns an unmodifiable view of currently cached query plans.
937 */
938 public Map<PBody, SubPlan> getCachedQueryPlans() {
939 return Collections.unmodifiableMap(plannerCache);
940 }
941
942 private QueryEvaluationHint getHints(SubPlan plan) {
943 return getHints(plan.getBody().getPattern());
944 }
945
946 private QueryEvaluationHint getHints(PQuery pattern) {
947 return hintProvider.getQueryEvaluationHint(pattern);
948 }
949}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.construction.quasitree;
11
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.List;
15import java.util.Map;
16import java.util.Set;
17import java.util.stream.Collectors;
18import java.util.stream.Stream;
19
20import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
21import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory;
22import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper;
23import tools.refinery.viatra.runtime.matchers.planning.operations.PJoin;
24import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
25import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
26import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
27import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
28
29/**
30 * @author Gabor Bergmann
31 *
32 */
33class JoinCandidate {
34 private QueryAnalyzer analyzer;
35
36 SubPlan primary;
37 SubPlan secondary;
38
39 Set<PVariable> varPrimary;
40 Set<PVariable> varSecondary;
41 Set<PVariable> varCommon;
42
43 List<PConstraint> consPrimary;
44 List<PConstraint> consSecondary;
45
46
47 JoinCandidate(SubPlan primary, SubPlan secondary, QueryAnalyzer analyzer) {
48 super();
49 this.primary = primary;
50 this.secondary = secondary;
51 this.analyzer = analyzer;
52
53 varPrimary = getPrimary().getVisibleVariables();
54 varSecondary = getSecondary().getVisibleVariables();
55 varCommon = CollectionsFactory.createSet(varPrimary);
56 varCommon.retainAll(varSecondary);
57
58 consPrimary = new ArrayList<PConstraint>(primary.getAllEnforcedConstraints());
59 Collections.sort(consPrimary, TieBreaker.CONSTRAINT_COMPARATOR);
60 consSecondary = new ArrayList<PConstraint>(secondary.getAllEnforcedConstraints());
61 Collections.sort(consSecondary, TieBreaker.CONSTRAINT_COMPARATOR);
62 }
63
64
65
66 /**
67 * @return the a
68 */
69 public SubPlan getPrimary() {
70 return primary;
71 }
72
73 /**
74 * @return the b
75 */
76 public SubPlan getSecondary() {
77 return secondary;
78 }
79
80 public SubPlan getJoinedPlan(SubPlanFactory factory) {
81 // check special cases first
82 if (isTrivial())
83 return primary;
84 if (isSubsumption())
85 return
86 (consPrimary.size() > consSecondary.size()) ? primary : secondary;
87
88
89 // default case
90 return factory.createSubPlan(new PJoin(), primary, secondary);
91 }
92
93 @Override
94 public String toString() {
95 return primary.toString() + " |x| " + secondary.toString();
96 }
97
98 /**
99 * @return the varPrimary
100 */
101 public Set<PVariable> getVarPrimary() {
102 return varPrimary;
103 }
104
105 /**
106 * @return the varSecondary
107 */
108 public Set<PVariable> getVarSecondary() {
109 return varSecondary;
110 }
111
112 /**
113 * @return constraints of primary, sorted according to {@link TieBreaker#CONSTRAINT_COMPARATOR}.
114 */
115 public List<PConstraint> getConsPrimary() {
116 return consPrimary;
117 }
118 /**
119 * @return constraints of secondary, sorted according to {@link TieBreaker#CONSTRAINT_COMPARATOR}.
120 */
121 public List<PConstraint> getConsSecondary() {
122 return consSecondary;
123 }
124
125
126
127 public boolean isTrivial() {
128 return getPrimary().equals(getSecondary());
129 }
130
131 public boolean isSubsumption() {
132 return consPrimary.containsAll(consSecondary) || consSecondary.containsAll(consPrimary);
133 }
134
135 public boolean isCheckOnly() {
136 return varPrimary.containsAll(varSecondary) || varSecondary.containsAll(varPrimary);
137 }
138
139 public boolean isDescartes() {
140 return Collections.disjoint(varPrimary, varSecondary);
141 }
142
143 private Boolean heath;
144
145 // it is a Heath-join iff common variables functionally determine either all primary or all secondary variables
146 public boolean isHeath() {
147 if (heath == null) {
148 Set<PConstraint> union = Stream.concat(
149 primary.getAllEnforcedConstraints().stream(),
150 secondary.getAllEnforcedConstraints().stream()
151 ).collect(Collectors.toSet());
152 Map<Set<PVariable>, Set<PVariable>> dependencies =
153 analyzer.getFunctionalDependencies(union, false);
154 // does varCommon determine either varPrimary or varSecondary?
155 Set<PVariable> varCommonClosure = FunctionalDependencyHelper.closureOf(varCommon, dependencies);
156
157 heath = varCommonClosure.containsAll(varPrimary) || varCommonClosure.containsAll(varSecondary);
158 }
159 return heath;
160 }
161
162}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.construction.quasitree;
11
12import java.util.Comparator;
13
14import tools.refinery.viatra.runtime.rete.util.Options;
15import tools.refinery.viatra.runtime.rete.util.OrderingCompareAgent;
16
17/**
18 * @author Gabor Bergmann
19 *
20 */
21public class JoinOrderingHeuristics implements Comparator<JoinCandidate> {
22
23 @Override
24 public int compare(JoinCandidate jc1, JoinCandidate jc2) {
25 return new OrderingCompareAgent<JoinCandidate>(jc1, jc2) {
26 @Override
27 protected void doCompare() {
28 swallowBoolean(true && consider(preferTrue(a.isTrivial(), b.isTrivial()))
29 && consider(preferTrue(a.isSubsumption(), b.isSubsumption()))
30 && consider(preferTrue(a.isCheckOnly(), b.isCheckOnly()))
31 && consider(
32 Options.functionalDependencyOption == Options.FunctionalDependencyOption.OFF ?
33 dontCare() :
34 preferTrue(a.isHeath(), b.isHeath())
35 )
36 && consider(preferFalse(a.isDescartes(), b.isDescartes()))
37
38 // TODO main heuristic decisions
39
40 // tie breaking
41 && consider(preferLess(a.getConsPrimary(), b.getConsPrimary(), TieBreaker.CONSTRAINT_LIST_COMPARATOR))
42 && consider(preferLess(a.getConsSecondary(), b.getConsSecondary(), TieBreaker.CONSTRAINT_LIST_COMPARATOR))
43 && consider(preferLess(System.identityHashCode(a), System.identityHashCode(b))));
44 }
45 }.compare();
46
47 }
48
49} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.construction.quasitree;
11
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.LinkedHashSet;
15import java.util.List;
16import java.util.Set;
17
18import org.apache.log4j.Logger;
19import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
20import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
21import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
22import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
23import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
24import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy;
25import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
26import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory;
27import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper;
28import tools.refinery.viatra.runtime.matchers.planning.operations.PApply;
29import tools.refinery.viatra.runtime.matchers.planning.operations.PEnumerate;
30import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
31import tools.refinery.viatra.runtime.matchers.planning.operations.PStart;
32import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint;
33import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint;
34import tools.refinery.viatra.runtime.matchers.psystem.PBody;
35import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer;
36import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue;
37import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
38import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException;
39import tools.refinery.viatra.runtime.rete.util.ReteHintOptions;
40
41/**
42 * Layout ideas: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=398763
43 *
44 * @author Gabor Bergmann
45 *
46 */
47public class QuasiTreeLayout implements IQueryPlannerStrategy {
48
49 private IQueryBackendHintProvider hintProvider;
50 private IQueryBackendContext backendContext;
51 private QueryAnalyzer queryAnalyzer;
52
53 public QuasiTreeLayout(IQueryBackendContext backendContext) {
54 this(backendContext, backendContext.getHintProvider());
55 }
56
57 public QuasiTreeLayout(IQueryBackendContext backendContext, IQueryBackendHintProvider hintProvider) {
58 this.backendContext = backendContext;
59 this.hintProvider = hintProvider;
60 queryAnalyzer = backendContext.getQueryAnalyzer();
61 }
62
63 @Override
64 public SubPlan plan(PBody pSystem, Logger logger, IQueryMetaContext context) {
65 return new Scaffold(pSystem, logger, context).run();
66 }
67
68 public class Scaffold {
69 PBody pSystem;
70 PQuery query;
71 IQueryMetaContext context;
72 private QueryEvaluationHint hints;
73 //IOperationCompiler compiler;
74 //SubPlanProcessor planProcessor = new SubPlanProcessor();
75 SubPlanFactory planFactory;
76
77 Set<DeferredPConstraint> deferredConstraints = null;
78 Set<EnumerablePConstraint> enumerableConstraints = null;
79 Set<ConstantValue> constantConstraints = null;
80 Set<SubPlan> forefront = new LinkedHashSet<SubPlan>();
81 Logger logger;
82
83 Scaffold(PBody pSystem, Logger logger, /*IOperationCompiler compiler,*/ IQueryMetaContext context) {
84 this.pSystem = pSystem;
85 this.logger = logger;
86 this.context = context;
87 this.planFactory = new SubPlanFactory(pSystem);
88 query = pSystem.getPattern();
89 //this.compiler = compiler;
90 //planProcessor.setCompiler(compiler);
91
92 hints = hintProvider.getQueryEvaluationHint(query);
93 }
94
95 /**
96 * @throws ViatraQueryRuntimeException
97 */
98 public SubPlan run() {
99 try {
100 logger.debug(String.format(
101 "%s: patternbody build started for %s",
102 getClass().getSimpleName(),
103 query.getFullyQualifiedName()));
104
105 // PROCESS CONSTRAINTS
106 deferredConstraints = pSystem.getConstraintsOfType(DeferredPConstraint.class);
107 enumerableConstraints = pSystem.getConstraintsOfType(EnumerablePConstraint.class);
108 constantConstraints = pSystem.getConstraintsOfType(ConstantValue.class);
109
110 for (EnumerablePConstraint enumerable : enumerableConstraints) {
111 SubPlan plan = planFactory.createSubPlan(new PEnumerate(enumerable));
112 admitSubPlan(plan);
113 }
114 if (enumerableConstraints.isEmpty()) { // EXTREME CASE
115 SubPlan plan = planFactory.createSubPlan(new PStart());
116 admitSubPlan(plan);
117 }
118
119 // JOIN FOREFRONT PLANS WHILE POSSIBLE
120 while (forefront.size() > 1) {
121 // TODO QUASI-TREE TRIVIAL JOINS?
122
123 List<JoinCandidate> candidates = generateJoinCandidates();
124 JoinOrderingHeuristics ordering = new JoinOrderingHeuristics();
125 JoinCandidate selectedJoin = Collections.min(candidates, ordering);
126 doJoin(selectedJoin);
127 }
128 assert (forefront.size() == 1);
129
130 // PROJECT TO PARAMETERS
131 SubPlan preFinalPlan = forefront.iterator().next();
132 SubPlan finalPlan = planFactory.createSubPlan(new PProject(pSystem.getSymbolicParameterVariables()), preFinalPlan);
133
134 // FINAL CHECK, whether all exported variables are present + all constraint satisfied
135 BuildHelper.finalCheck(pSystem, finalPlan, context);
136 // TODO integrate the check above in SubPlan / POperation
137
138 logger.debug(String.format(
139 "%s: patternbody query plan concluded for %s as: %s",
140 getClass().getSimpleName(),
141 query.getFullyQualifiedName(),
142 finalPlan.toLongString()));
143 return finalPlan;
144 } catch (RetePatternBuildException ex) {
145 ex.setPatternDescription(query);
146 throw ex;
147 }
148 }
149
150 public List<JoinCandidate> generateJoinCandidates() {
151 List<JoinCandidate> candidates = new ArrayList<JoinCandidate>();
152 int bIndex = 0;
153 for (SubPlan b : forefront) {
154 int aIndex = 0;
155 for (SubPlan a : forefront) {
156 if (aIndex++ >= bIndex)
157 break;
158 candidates.add(new JoinCandidate(a, b, queryAnalyzer));
159 }
160 bIndex++;
161 }
162 return candidates;
163 }
164
165 private void admitSubPlan(SubPlan plan) {
166 // are there any unapplied constant filters that we can apply here?
167 if (ReteHintOptions.prioritizeConstantFiltering.getValueOrDefault(hints)) {
168 for (ConstantValue constantConstraint : constantConstraints) {
169 if (!plan.getAllEnforcedConstraints().contains(constantConstraint) &&
170 plan.getVisibleVariables().containsAll(constantConstraint.getAffectedVariables())) {
171 plan = planFactory.createSubPlan(new PApply(constantConstraint), plan);
172 }
173 }
174 }
175 // are there any variables that will not be needed anymore and are worth trimming?
176 // (check only if there are unenforced enumerables, so that there are still upcoming joins)
177// if (Options.planTrimOption != Options.PlanTrimOption.OFF &&
178// !plan.getAllEnforcedConstraints().containsAll(enumerableConstraints)) {
179 if (true) {
180 final SubPlan trimmed = BuildHelper.trimUnneccessaryVariables(
181 planFactory, plan, true, queryAnalyzer);
182 plan = trimmed;
183 }
184 // are there any checkable constraints?
185 for (DeferredPConstraint deferred : deferredConstraints) {
186 if (!plan.getAllEnforcedConstraints().contains(deferred)) {
187 if (deferred.isReadyAt(plan, context)) {
188 admitSubPlan(planFactory.createSubPlan(new PApply(deferred), plan));
189 return;
190 }
191 }
192 }
193 // if no checkable constraints and no unused variables
194 forefront.add(plan);
195 }
196
197 private void doJoin(JoinCandidate selectedJoin) {
198 forefront.remove(selectedJoin.getPrimary());
199 forefront.remove(selectedJoin.getSecondary());
200 admitSubPlan(selectedJoin.getJoinedPlan(planFactory));
201 }
202
203 }
204
205}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.construction.quasitree;
10
11import java.util.Comparator;
12
13import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
14import tools.refinery.viatra.runtime.rete.util.LexicographicComparator;
15
16/**
17 * Class providing comparators for breaking ties somewhat more deterministically.
18 * @author Bergmann Gabor
19 *
20 */
21public class TieBreaker {
22
23 private TieBreaker() {/*Utility class constructor*/}
24
25 public static final Comparator<PConstraint> CONSTRAINT_COMPARATOR = (arg0, arg1) -> arg0.getMonotonousID() - arg1.getMonotonousID();
26
27 public static final Comparator<Iterable<? extends PConstraint>> CONSTRAINT_LIST_COMPARATOR =
28 new LexicographicComparator<PConstraint>(CONSTRAINT_COMPARATOR);
29
30}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import java.util.Iterator;
12
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.util.Direction;
15import tools.refinery.viatra.runtime.rete.network.ReteContainer;
16import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
17import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
18
19/**
20 * @author Bergmann Gabor
21 */
22public abstract class AbstractEvaluatorNode extends SingleInputNode implements IEvaluatorNode {
23
24 /**
25 * @since 1.5
26 */
27 protected EvaluatorCore core;
28
29
30 /**
31 * @since 1.5
32 */
33 public AbstractEvaluatorNode(ReteContainer reteContainer, EvaluatorCore core) {
34 super(reteContainer);
35 this.core = core;
36 core.init(this);
37 }
38
39 /**
40 * @since 1.5
41 */
42 @Override
43 public ReteContainer getReteContainer() {
44 return getContainer();
45 }
46
47 /**
48 * @since 1.5
49 */
50 @Override
51 public String prettyPrintTraceInfoPatternList() {
52 return getTraceInfoPatternsEnumerated();
53 }
54
55 /**
56 * @since 2.4
57 */
58 protected void propagateIterableUpdate(final Direction direction, final Iterable<Tuple> update, final Timestamp timestamp) {
59 final Iterator<Tuple> itr = update.iterator();
60 while (itr.hasNext()) {
61 propagateUpdate(direction, itr.next(), timestamp);
62 }
63 }
64
65}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.Map;
14import java.util.Set;
15
16import org.apache.log4j.Logger;
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.tuple.TupleValueProvider;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
22import tools.refinery.viatra.runtime.matchers.util.Sets;
23
24/**
25 * An instance of this class performs the evaluation of Java expressions.
26 *
27 * @author Bergmann Gabor
28 * @author Tamas Szabo
29 * @since 1.5
30 */
31public abstract class EvaluatorCore {
32
33 protected Logger logger;
34 protected IExpressionEvaluator evaluator;
35 /**
36 * @since 2.4
37 */
38 protected int sourceTupleWidth;
39 private Map<String, Integer> parameterPositions;
40 protected IQueryRuntimeContext runtimeContext;
41 protected IEvaluatorNode evaluatorNode;
42
43 public EvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator,
44 final Map<String, Integer> parameterPositions, final int sourceTupleWidth) {
45 this.logger = logger;
46 this.evaluator = evaluator;
47 this.parameterPositions = parameterPositions;
48 this.sourceTupleWidth = sourceTupleWidth;
49 }
50
51 public void init(final IEvaluatorNode evaluatorNode) {
52 this.evaluatorNode = evaluatorNode;
53 this.runtimeContext = evaluatorNode.getReteContainer().getNetwork().getEngine().getRuntimeContext();
54 }
55
56 /**
57 * @since 2.4
58 */
59 public abstract Iterable<Tuple> performEvaluation(final Tuple input);
60
61 protected abstract String evaluationKind();
62
63 public Object evaluateTerm(final Tuple input) {
64 // actual evaluation
65 Object result = null;
66 try {
67 final TupleValueProvider tupleParameters = new TupleValueProvider(runtimeContext.unwrapTuple(input),
68 parameterPositions);
69 result = evaluator.evaluateExpression(tupleParameters);
70 } catch (final Exception e) {
71 logger.warn(String.format(
72 "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)",
73 evaluationKind(), evaluatorNode.prettyPrintTraceInfoPatternList(), prettyPrintTuple(input),
74 e.getMessage(), e.getClass().getSimpleName(), this.evaluatorNode), e);
75 result = errorResult();
76 }
77
78 return result;
79 }
80
81 protected String prettyPrintTuple(final Tuple tuple) {
82 return tuple.toString();
83 }
84
85 protected Object errorResult() {
86 return null;
87 }
88
89 public static class PredicateEvaluatorCore extends EvaluatorCore {
90
91 public PredicateEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator,
92 final Map<String, Integer> parameterPositions, final int sourceTupleWidth) {
93 super(logger, evaluator, parameterPositions, sourceTupleWidth);
94 }
95
96 @Override
97 public Iterable<Tuple> performEvaluation(final Tuple input) {
98 final Object result = evaluateTerm(input);
99 if (Boolean.TRUE.equals(result)) {
100 return Collections.singleton(input);
101 } else {
102 return null;
103 }
104 }
105
106 @Override
107 protected String evaluationKind() {
108 return "check()";
109 }
110
111 }
112
113 public static class FunctionEvaluatorCore extends EvaluatorCore {
114
115 /**
116 * @since 2.4
117 */
118 protected final boolean isUnwinding;
119
120 public FunctionEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator,
121 final Map<String, Integer> parameterPositions, final int sourceTupleWidth) {
122 this(logger, evaluator, parameterPositions, sourceTupleWidth, false);
123 }
124
125 /**
126 * @since 2.4
127 */
128 public FunctionEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator,
129 final Map<String, Integer> parameterPositions, final int sourceTupleWidth, final boolean isUnwinding) {
130 super(logger, evaluator, parameterPositions, sourceTupleWidth);
131 this.isUnwinding = isUnwinding;
132 }
133
134 @Override
135 public Iterable<Tuple> performEvaluation(final Tuple input) {
136 final Object result = evaluateTerm(input);
137 if (result != null) {
138 if (this.isUnwinding) {
139 final Set<?> resultAsSet = (result instanceof Set<?>) ? (Set<?>) result
140 : (result instanceof Iterable<?>) ? Sets.newSet((Iterable<?>) result) : null;
141
142 if (resultAsSet != null) {
143 return () -> {
144 final Iterator<?> wrapped = resultAsSet.iterator();
145 return new Iterator<Tuple>() {
146 @Override
147 public boolean hasNext() {
148 return wrapped.hasNext();
149 }
150
151 @Override
152 public Tuple next() {
153 final Object next = wrapped.next();
154 return Tuples.staticArityLeftInheritanceTupleOf(input,
155 runtimeContext.wrapElement(next));
156 }
157 };
158 };
159 } else {
160 throw new IllegalStateException(
161 "This is an unwinding evaluator, which expects the evaluation result to either be a set or an iterable, but it was "
162 + result);
163 }
164 } else {
165 return Collections.singleton(
166 Tuples.staticArityLeftInheritanceTupleOf(input, runtimeContext.wrapElement(result)));
167 }
168 } else {
169 return null;
170 }
171 }
172
173 @Override
174 protected String evaluationKind() {
175 return "eval" + (this.isUnwinding ? "Unwind" : "") + "()";
176 }
177
178 }
179
180}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import tools.refinery.viatra.runtime.rete.network.ReteContainer;
12
13/**
14 * This interface is required for the communication between the evaluation core end the evaluator node.
15 * @author Gabor Bergmann
16 * @since 1.5
17 */
18public interface IEvaluatorNode {
19
20 ReteContainer getReteContainer();
21
22 String prettyPrintTraceInfoPatternList();
23
24
25}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Iterator;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23
24/**
25 * @author Bergmann Gabor
26 *
27 */
28public class MemorylessEvaluatorNode extends AbstractEvaluatorNode {
29
30 /**
31 * @since 1.5
32 */
33 public MemorylessEvaluatorNode(final ReteContainer reteContainer, final EvaluatorCore core) {
34 super(reteContainer, core);
35 }
36
37 @Override
38 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
39 final Collection<Tuple> parentTuples = new ArrayList<Tuple>();
40 propagatePullInto(parentTuples, flush);
41 for (final Tuple parentTuple : parentTuples) {
42 final Iterable<Tuple> output = core.performEvaluation(parentTuple);
43 if (output != null) {
44 final Iterator<Tuple> itr = output.iterator();
45 while (itr.hasNext()) {
46 collector.add(itr.next());
47 }
48 }
49 }
50 }
51
52 @Override
53 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
54 final Map<Tuple, Timeline<Timestamp>> parentTuples = CollectionsFactory.createMap();
55 propagatePullIntoWithTimestamp(parentTuples, flush);
56 for (final Entry<Tuple, Timeline<Timestamp>> entry : parentTuples.entrySet()) {
57 final Iterable<Tuple> output = core.performEvaluation(entry.getKey());
58 if (output != null) {
59 final Iterator<Tuple> itr = output.iterator();
60 while (itr.hasNext()) {
61 collector.put(itr.next(), entry.getValue());
62 }
63 }
64 }
65 }
66
67 @Override
68 public void update(final Direction direction, final Tuple input, final Timestamp timestamp) {
69 final Iterable<Tuple> output = core.performEvaluation(input);
70 if (output != null) {
71 propagateIterableUpdate(direction, output, timestamp);
72 }
73 }
74
75}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
19import tools.refinery.viatra.runtime.matchers.util.Clearable;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Direction;
22import tools.refinery.viatra.runtime.matchers.util.Signed;
23import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
25import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
26import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
27import tools.refinery.viatra.runtime.rete.network.ReteContainer;
28import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
29import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
30import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
31
32/**
33 * An evaluator node that caches the evaluation result. This node is also capable of caching the timestamps associated
34 * with the result tuples if it is used in recursive differential dataflow evaluation.
35 *
36 * @author Bergmann Gabor
37 * @author Tamas Szabo
38 */
39public class OutputCachingEvaluatorNode extends AbstractEvaluatorNode implements Clearable, ResumableNode {
40
41 /**
42 * @since 2.3
43 */
44 protected NetworkStructureChangeSensitiveLogic logic;
45
46 /**
47 * @since 2.4
48 */
49 protected Map<Tuple, Iterable<Tuple>> outputCache;
50
51 /**
52 * Maps input tuples to timestamps. It is wrong to map evaluation result to timestamps because the different input
53 * tuples may yield the same evaluation result. This field is null as long as this node is in a non-recursive group.
54 *
55 * @since 2.4
56 */
57 protected TimelyMemory<Timestamp> memory;
58
59 /**
60 * @since 2.4
61 */
62 protected CommunicationGroup group;
63
64 /**
65 * @since 1.5
66 */
67 public OutputCachingEvaluatorNode(final ReteContainer reteContainer, final EvaluatorCore core) {
68 super(reteContainer, core);
69 reteContainer.registerClearable(this);
70 this.outputCache = CollectionsFactory.createMap();
71 this.logic = createLogic();
72 }
73
74 @Override
75 public CommunicationGroup getCurrentGroup() {
76 return this.group;
77 }
78
79 @Override
80 public void setCurrentGroup(final CommunicationGroup group) {
81 this.group = group;
82 }
83
84 @Override
85 public void networkStructureChanged() {
86 super.networkStructureChanged();
87 this.logic = createLogic();
88 }
89
90 @Override
91 public void clear() {
92 this.outputCache.clear();
93 if (this.memory != null) {
94 this.memory.clear();
95 }
96 }
97
98 /**
99 * @since 2.3
100 */
101 protected NetworkStructureChangeSensitiveLogic createLogic() {
102 if (this.reteContainer.isTimelyEvaluation()
103 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
104 if (this.memory == null) {
105 this.memory = new TimelyMemory<Timestamp>(reteContainer.isTimelyEvaluation() && reteContainer
106 .getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL);
107 }
108 return TIMELY;
109 } else {
110 return TIMELESS;
111 }
112 }
113
114 @Override
115 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
116 this.logic.pullInto(collector, flush);
117 }
118
119 @Override
120 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
121 this.logic.pullIntoWithTimeline(collector, flush);
122 }
123
124 @Override
125 public void update(final Direction direction, final Tuple input, final Timestamp timestamp) {
126 this.logic.update(direction, input, timestamp);
127 }
128
129 /**
130 * @since 2.4
131 */
132 @Override
133 public Timestamp getResumableTimestamp() {
134 if (this.memory == null) {
135 return null;
136 } else {
137 return this.memory.getResumableTimestamp();
138 }
139 }
140
141 /**
142 * @since 2.4
143 */
144 @Override
145 public void resumeAt(final Timestamp timestamp) {
146 this.logic.resumeAt(timestamp);
147 }
148
149 /**
150 * @since 2.3
151 */
152 protected static abstract class NetworkStructureChangeSensitiveLogic {
153
154 /**
155 * @since 2.4
156 */
157 public abstract void update(final Direction direction, final Tuple input, final Timestamp timestamp);
158
159 public abstract void pullInto(final Collection<Tuple> collector, final boolean flush);
160
161 /**
162 * @since 2.4
163 */
164 public abstract void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush);
165
166 /**
167 * @since 2.4
168 */
169 public abstract void resumeAt(final Timestamp timestamp);
170
171 }
172
173 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
174
175 @Override
176 public void resumeAt(final Timestamp timestamp) {
177 // there is nothing to resume in the timeless case because we do not even care about timestamps
178 }
179
180 @Override
181 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
182 throw new UnsupportedOperationException();
183 }
184
185 @Override
186 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
187 for (final Iterable<Tuple> output : outputCache.values()) {
188 if (output != NORESULT) {
189 final Iterator<Tuple> itr = output.iterator();
190 while (itr.hasNext()) {
191 collector.add(itr.next());
192 }
193 }
194 }
195 }
196
197 @Override
198 public void update(final Direction direction, final Tuple input, final Timestamp timestamp) {
199 if (direction == Direction.INSERT) {
200 final Iterable<Tuple> output = core.performEvaluation(input);
201 if (output != null) {
202 final Iterable<Tuple> previous = outputCache.put(input, output);
203 if (previous != null) {
204 throw new IllegalStateException(
205 String.format("Duplicate insertion of tuple %s into node %s", input, this));
206 }
207 propagateIterableUpdate(direction, output, timestamp);
208 }
209 } else {
210 final Iterable<Tuple> output = outputCache.remove(input);
211 if (output != null) {
212 // may be null if no result was yielded
213 propagateIterableUpdate(direction, output, timestamp);
214 }
215 }
216 }
217 };
218
219 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
220
221 @Override
222 public void resumeAt(final Timestamp timestamp) {
223 final Map<Tuple, Diff<Timestamp>> diffMap = memory.resumeAt(timestamp);
224
225 for (final Entry<Tuple, Diff<Timestamp>> entry : diffMap.entrySet()) {
226 final Tuple input = entry.getKey();
227 final Iterable<Tuple> output = outputCache.get(input);
228 if (output != NORESULT) {
229 for (final Signed<Timestamp> signed : entry.getValue()) {
230 propagateIterableUpdate(signed.getDirection(), output, signed.getPayload());
231 }
232 }
233
234 if (memory.get(input) == null) {
235 outputCache.remove(input);
236 }
237 }
238
239 final Timestamp nextTimestamp = memory.getResumableTimestamp();
240 if (nextTimestamp != null) {
241 group.notifyHasMessage(mailbox, nextTimestamp);
242 }
243 }
244
245 @Override
246 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
247 for (final Entry<Tuple, Timeline<Timestamp>> entry : memory.asMap().entrySet()) {
248 final Tuple input = entry.getKey();
249 final Iterable<Tuple> output = outputCache.get(input);
250 if (output != NORESULT) {
251 final Timeline<Timestamp> timestamp = entry.getValue();
252 final Iterator<Tuple> itr = output.iterator();
253 while (itr.hasNext()) {
254 collector.put(itr.next(), timestamp);
255 }
256 }
257 }
258 }
259
260 @Override
261 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
262 TIMELESS.pullInto(collector, flush);
263 }
264
265 @Override
266 public void update(final Direction direction, final Tuple input, final Timestamp timestamp) {
267 if (direction == Direction.INSERT) {
268 Iterable<Tuple> output = outputCache.get(input);
269 if (output == null) {
270 output = core.performEvaluation(input);
271 if (output == null) {
272 // the evaluation result is really null
273 output = NORESULT;
274 }
275 outputCache.put(input, output);
276 }
277 final Diff<Timestamp> diff = memory.put(input, timestamp);
278 if (output != NORESULT) {
279 for (final Signed<Timestamp> signed : diff) {
280 propagateIterableUpdate(signed.getDirection(), output, signed.getPayload());
281 }
282 }
283 } else {
284 final Iterable<Tuple> output = outputCache.get(input);
285 final Diff<Timestamp> diff = memory.remove(input, timestamp);
286 if (memory.get(input) == null) {
287 outputCache.remove(input);
288 }
289 if (output != NORESULT) {
290 for (final Signed<Timestamp> signed : diff) {
291 propagateIterableUpdate(signed.getDirection(), output, signed.getPayload());
292 }
293 }
294 }
295 }
296 };
297
298 /**
299 * This field is used to represent the "null" evaluation result. This is an optimization used in the timely case
300 * where the same tuple may be inserted multiple times with different timestamps. This way, we can also cache if
301 * something evaluated to null (instead of just forgetting about the previously computed result), thus avoiding the
302 * need to re-run a potentially expensive evaluation.
303 */
304 private static final Iterable<Tuple> NORESULT = Collections
305 .singleton(Tuples.staticArityFlatTupleOf(NoResult.INSTANCE));
306
307 private enum NoResult {
308 INSTANCE
309 }
310
311}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2022, Tamas Szabo, GitHub
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.List;
14import java.util.Map;
15import java.util.Map.Entry;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator;
19import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.RelationEvaluation;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
21import tools.refinery.viatra.runtime.matchers.util.Clearable;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines;
25import tools.refinery.viatra.runtime.rete.misc.SimpleReceiver;
26import tools.refinery.viatra.runtime.rete.network.ProductionNode;
27import tools.refinery.viatra.runtime.rete.network.Receiver;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.StandardNode;
30import tools.refinery.viatra.runtime.rete.network.Supplier;
31import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
32import tools.refinery.viatra.runtime.rete.single.AbstractUniquenessEnforcerNode;
33
34/**
35 * A node that operates in batch-style (see {@link Receiver#doesProcessUpdatesInBatch()} and evaluates arbitrary Java
36 * logic represented by an {@link IRelationEvaluator} on the input relations. This is the backing computation node of a
37 * {@link RelationEvaluation} constraint.
38 *
39 * @author Tamas Szabo
40 * @since 2.8
41 */
42public class RelationEvaluatorNode extends StandardNode implements Supplier, Clearable {
43
44 private final IRelationEvaluator evaluator;
45 private Set<Tuple> cachedOutputs;
46 private Supplier[] inputSuppliers;
47 private BatchingReceiver[] inputReceivers;
48
49 public RelationEvaluatorNode(final ReteContainer container, final IRelationEvaluator evaluator) {
50 super(container);
51 this.evaluator = evaluator;
52 this.reteContainer.registerClearable(this);
53 }
54
55 @Override
56 public void clear() {
57 this.cachedOutputs.clear();
58 }
59
60 public void connectToParents(final List<Supplier> inputSuppliers) {
61 this.inputSuppliers = new Supplier[inputSuppliers.size()];
62 this.inputReceivers = new BatchingReceiver[inputSuppliers.size()];
63
64 final List<Integer> inputArities = evaluator.getInputArities();
65
66 if (inputArities.size() != inputSuppliers.size()) {
67 throw new IllegalStateException(evaluator.toString() + " expects " + inputArities.size()
68 + " inputs, but got " + inputSuppliers.size() + " input(s)!");
69 }
70
71 for (int i = 0; i < inputSuppliers.size(); i++) {
72 final int currentExpectedInputArity = inputArities.get(i);
73 final Supplier inputSupplier = inputSuppliers.get(i);
74 // it is expected that the supplier is a production node because
75 // the corresponding constraint itself accepts a list of PQuery
76 if (!(inputSupplier instanceof ProductionNode)) {
77 throw new IllegalStateException(
78 evaluator.toString() + " expects each one of its suppliers to be instances of "
79 + ProductionNode.class.getSimpleName() + " but got an instance of "
80 + inputSupplier.getClass().getSimpleName() + "!");
81 }
82 final int currentActualInputArity = ((ProductionNode) inputSupplier).getPosMapping().size();
83 if (currentActualInputArity != currentExpectedInputArity) {
84 throw new IllegalStateException(
85 evaluator.toString() + " expects input arity " + currentExpectedInputArity + " at position " + i
86 + " but got " + currentActualInputArity + "!");
87 }
88 final BatchingReceiver inputReceiver = new BatchingReceiver((ProductionNode) inputSupplier,
89 this.reteContainer);
90 this.inputSuppliers[i] = inputSupplier;
91 this.inputReceivers[i] = inputReceiver;
92 this.reteContainer.connectAndSynchronize(inputSupplier, inputReceiver);
93 reteContainer.getCommunicationTracker().registerDependency(inputReceiver, this);
94 }
95
96 // initialize the output relation
97 final List<Set<Tuple>> inputSets = new ArrayList<Set<Tuple>>();
98 for (final BatchingReceiver inputReceiver : this.inputReceivers) {
99 inputSets.add(inputReceiver.getTuples());
100 }
101 this.cachedOutputs = evaluateRelation(inputSets);
102 }
103
104 @Override
105 public void networkStructureChanged() {
106 if (this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
107 throw new IllegalStateException(this.toString() + " cannot be used in recursive evaluation!");
108 }
109 super.networkStructureChanged();
110 }
111
112 @Override
113 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
114 collector.addAll(this.cachedOutputs);
115 }
116
117 @Override
118 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
119 final Timeline<Timestamp> timeline = Timelines.createFrom(Timestamp.ZERO);
120 for (final Tuple output : this.cachedOutputs) {
121 collector.put(output, timeline);
122 }
123 }
124
125 private Set<Tuple> evaluateRelation(final List<Set<Tuple>> inputs) {
126 try {
127 return this.evaluator.evaluateRelation(inputs);
128 } catch (final Exception e) {
129 throw new IllegalStateException("Exception during the evaluation of " + this.evaluator.toString() + "!", e);
130 }
131 }
132
133 private void batchUpdateCompleted() {
134 final List<Set<Tuple>> inputSets = new ArrayList<Set<Tuple>>();
135 for (final BatchingReceiver inputReceiver : this.inputReceivers) {
136 inputSets.add(inputReceiver.getTuples());
137 }
138 final Set<Tuple> newOutputs = evaluateRelation(inputSets);
139 for (final Tuple tuple : newOutputs) {
140 if (this.cachedOutputs != null && this.cachedOutputs.remove(tuple)) {
141 // already known tuple - do nothing
142 } else {
143 // newly inserted tuple
144 propagateUpdate(Direction.INSERT, tuple, Timestamp.ZERO);
145 }
146 }
147 if (this.cachedOutputs != null) {
148 for (final Tuple tuple : this.cachedOutputs) {
149 // lost tuple
150 propagateUpdate(Direction.DELETE, tuple, Timestamp.ZERO);
151 }
152 }
153 this.cachedOutputs = newOutputs;
154 }
155
156 public class BatchingReceiver extends SimpleReceiver {
157 private final ProductionNode source;
158
159 private BatchingReceiver(final ProductionNode source, final ReteContainer container) {
160 super(container);
161 this.source = source;
162 }
163
164 private Set<Tuple> getTuples() {
165 return ((AbstractUniquenessEnforcerNode) this.source).getTuples();
166 }
167
168 @Override
169 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
170 throw new UnsupportedOperationException("This receiver only supports batch-style operation!");
171 }
172
173 @Override
174 public void batchUpdate(final Collection<Entry<Tuple, Integer>> updates, final Timestamp timestamp) {
175 assert Timestamp.ZERO.equals(timestamp);
176 // there is nothing to do here because the source production node has already updated itself
177 // the only thing we need to do is to issue the callback
178 RelationEvaluatorNode.this.batchUpdateCompleted();
179 }
180
181 }
182
183}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.index;
10
11import java.lang.ref.WeakReference;
12
13import tools.refinery.viatra.runtime.rete.network.Node;
14
15public abstract class DefaultIndexerListener implements IndexerListener {
16
17 WeakReference<Node> owner;
18
19 public DefaultIndexerListener(Node owner) {
20 this.owner = new WeakReference<Node>(owner);
21 }
22
23 @Override
24 public Node getOwner() {
25 return owner.get();
26 }
27
28}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
21import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
22import tools.refinery.viatra.runtime.rete.network.Receiver;
23import tools.refinery.viatra.runtime.rete.network.ReteContainer;
24import tools.refinery.viatra.runtime.rete.network.StandardNode;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.communication.Timestamp.AllZeroMap;
27import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand;
28import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
29import tools.refinery.viatra.runtime.rete.util.Options;
30
31/**
32 * Abstract superclass for nodes with two inputs that are matched against each other.
33 *
34 * @author Gabor Bergmann
35 */
36public abstract class DualInputNode extends StandardNode implements NetworkStructureChangeSensitiveNode {
37
38 /**
39 * @since 2.3
40 */
41 protected NetworkStructureChangeSensitiveLogic logic;
42
43 public IterableIndexer getPrimarySlot() {
44 return primarySlot;
45 }
46
47 public Indexer getSecondarySlot() {
48 return secondarySlot;
49 }
50
51 /**
52 * @author Gabor Bergmann
53 *
54 */
55 public enum Side {
56 PRIMARY, SECONDARY, BOTH;
57
58 public Side opposite() {
59 switch (this) {
60 case PRIMARY:
61 return SECONDARY;
62 case SECONDARY:
63 return PRIMARY;
64 case BOTH:
65 return BOTH;
66 default:
67 return BOTH;
68 }
69 }
70 }
71
72 /**
73 * Holds the primary input slot of this node.
74 */
75 protected IterableIndexer primarySlot;
76
77 /**
78 * Holds the secondary input slot of this node.
79 */
80 protected Indexer secondarySlot;
81
82 /**
83 * Optional complementer mask
84 */
85 protected TupleMask complementerSecondaryMask;
86
87 /**
88 * true if the primary and secondary slots coincide
89 */
90 protected boolean coincidence;
91
92 /**
93 * @param reteContainer
94 */
95 public DualInputNode(final ReteContainer reteContainer, final TupleMask complementerSecondaryMask) {
96 super(reteContainer);
97 this.complementerSecondaryMask = complementerSecondaryMask;
98 this.indexerGroupCache = CollectionsFactory.createMap();
99 this.refreshIndexerGroupCache();
100 }
101
102 /**
103 * Should be called only once, when node is initialized.
104 */
105 public void connectToIndexers(final IterableIndexer primarySlot, final Indexer secondarySlot) {
106 this.primarySlot = primarySlot;
107 this.secondarySlot = secondarySlot;
108
109 reteContainer.getCommunicationTracker().registerDependency(primarySlot, this);
110 reteContainer.getCommunicationTracker().registerDependency(secondarySlot, this);
111
112 // attach listeners
113 // if there is syncing, do this after the flush done for pulling, but before syncing updates
114 coincidence = primarySlot.equals(secondarySlot);
115
116 if (!coincidence) { // regular case
117 primarySlot.attachListener(new DefaultIndexerListener(this) {
118 @Override
119 public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement,
120 final Tuple signature, final boolean change, final Timestamp timestamp) {
121 DualInputNode.this.logic.notifyUpdate(Side.PRIMARY, direction, updateElement, signature, change,
122 timestamp);
123 }
124
125 @Override
126 public String toString() {
127 return "primary@" + DualInputNode.this;
128 }
129 });
130 secondarySlot.attachListener(new DefaultIndexerListener(this) {
131 public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement,
132 final Tuple signature, final boolean change, final Timestamp timestamp) {
133 DualInputNode.this.logic.notifyUpdate(Side.SECONDARY, direction, updateElement, signature, change,
134 timestamp);
135 }
136
137 @Override
138 public String toString() {
139 return "secondary@" + DualInputNode.this;
140 }
141 });
142 } else { // if the two slots are the same, updates have to be handled carefully
143 primarySlot.attachListener(new DefaultIndexerListener(this) {
144 public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement,
145 final Tuple signature, final boolean change, final Timestamp timestamp) {
146 DualInputNode.this.logic.notifyUpdate(Side.BOTH, direction, updateElement, signature, change,
147 timestamp);
148 }
149
150 @Override
151 public String toString() {
152 return "both@" + DualInputNode.this;
153 }
154 });
155 }
156
157 for (final Receiver receiver : getReceivers()) {
158 this.reteContainer.getDelayedCommandQueue()
159 .add(new DelayedConnectCommand(this, receiver, this.reteContainer));
160 }
161
162 // Given that connectToIndexers registers new dependencies, the networkStructureChanged() method will be called
163 // by the CommunicationTracker, and the implementation of that method in turn will call refreshIndexerGroupCache() anyway.
164 this.refreshIndexerGroupCache();
165 }
166
167 /**
168 * Helper: retrieves all stored substitutions from the opposite side memory.
169 *
170 * @return the collection of opposite substitutions if any, or null if none
171 */
172 protected Collection<Tuple> retrieveOpposites(final Side side, final Tuple signature) {
173 return getSlot(side.opposite()).get(signature);
174 }
175
176 /**
177 * @since 2.3
178 */
179 protected NetworkStructureChangeSensitiveLogic createLogic() {
180 if (this.reteContainer.isTimelyEvaluation()
181 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
182 return createTimelyLogic();
183 } else {
184 return createTimelessLogic();
185 }
186 }
187
188 /**
189 * Helper: unifies a left and right partial matching.
190 */
191 protected Tuple unify(final Tuple left, final Tuple right) {
192 return complementerSecondaryMask.combine(left, right, Options.enableInheritance, true);
193 }
194
195 @Override
196 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
197 this.logic.pullInto(collector, flush);
198 }
199
200 @Override
201 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
202 this.logic.pullIntoWithTimeline(collector, flush);
203 }
204
205 /**
206 * Helper: unifies a substitution from the specified side with another substitution from the other side.
207 */
208 protected Tuple unify(final Side side, final Tuple ps, final Tuple opposite) {
209 switch (side) {
210 case PRIMARY:
211 return unify(ps, opposite);
212 case SECONDARY:
213 return unify(opposite, ps);
214 case BOTH:
215 return unify(ps, opposite);
216 default:
217 return null;
218 }
219 }
220
221 /**
222 * Simulates the behavior of the node for calibration purposes only.
223 */
224 public abstract Tuple calibrate(final Tuple primary, final Tuple secondary);
225
226 /**
227 * @param complementerSecondaryMask
228 * the complementerSecondaryMask to set
229 */
230 public void setComplementerSecondaryMask(final TupleMask complementerSecondaryMask) {
231 this.complementerSecondaryMask = complementerSecondaryMask;
232 }
233
234 /**
235 * Retrieves the slot corresponding to the specified side.
236 */
237 protected Indexer getSlot(final Side side) {
238 if (side == Side.SECONDARY) {
239 return secondarySlot;
240 } else {
241 return primarySlot;
242 }
243 }
244
245 @Override
246 public void assignTraceInfo(final TraceInfo traceInfo) {
247 super.assignTraceInfo(traceInfo);
248 if (traceInfo.propagateToIndexerParent()) {
249 if (primarySlot != null) {
250 primarySlot.acceptPropagatedTraceInfo(traceInfo);
251 }
252 if (secondarySlot != null) {
253 secondarySlot.acceptPropagatedTraceInfo(traceInfo);
254 }
255 }
256 }
257
258 @Override
259 public void networkStructureChanged() {
260 super.networkStructureChanged();
261 this.logic = createLogic();
262 this.refreshIndexerGroupCache();
263 }
264
265 /**
266 * @since 2.3
267 */
268 protected abstract NetworkStructureChangeSensitiveLogic createTimelyLogic();
269
270 /**
271 * @since 2.3
272 */
273 protected abstract NetworkStructureChangeSensitiveLogic createTimelessLogic();
274
275 /**
276 * This map caches the result of a CommunicationTracker.areInSameGroup(indexer, this) call. It does that for both
277 * the primary and secondary slots. This way we can avoid the lookup in the getWithTimestamp call for each tuple.
278 * The cache needs to be maintained when the network structure changes.
279 * @since 2.3
280 */
281 protected Map<Indexer, Boolean> indexerGroupCache;
282
283 /**
284 * @since 2.3
285 */
286 protected void refreshIndexerGroupCache() {
287 this.indexerGroupCache.clear();
288 if (this.primarySlot != null) {
289 this.indexerGroupCache.put(this.primarySlot,
290 this.reteContainer.getCommunicationTracker().areInSameGroup(this.primarySlot, this));
291 }
292 if (this.secondarySlot != null) {
293 this.indexerGroupCache.put(this.secondarySlot,
294 this.reteContainer.getCommunicationTracker().areInSameGroup(this.secondarySlot, this));
295 }
296 }
297
298 /**
299 * @since 2.4
300 */
301 protected Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature, final Indexer indexer) {
302 if (this.indexerGroupCache.get(indexer)) {
303 // recursive timely case
304 return indexer.getTimeline(signature);
305 } else {
306 // the indexer is in a different group, treat all of its tuples as they would have timestamp 0
307 final Collection<Tuple> tuples = indexer.get(signature);
308 if (tuples == null) {
309 return null;
310 } else {
311 return new AllZeroMap<Tuple>((Set<Tuple>) tuples);
312 }
313 }
314 }
315
316 /**
317 * @since 2.3
318 */
319 protected static abstract class NetworkStructureChangeSensitiveLogic {
320
321 /**
322 * Abstract handler for update event.
323 *
324 * @param side
325 * The side on which the event occurred.
326 * @param direction
327 * The direction of the update.
328 * @param updateElement
329 * The partial matching that is inserted.
330 * @param signature
331 * Masked signature of updateElement.
332 * @param change
333 * Indicates whether this is/was the first/last instance of this signature in this slot.
334 * @since 2.4
335 */
336 public abstract void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement,
337 final Tuple signature, final boolean change, final Timestamp timestamp);
338
339 public abstract void pullInto(final Collection<Tuple> collector, final boolean flush);
340
341 /**
342 * @since 2.4
343 */
344 public abstract void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush);
345
346 }
347
348}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.matchers.util.Signed;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.network.ReteContainer;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22/**
23 * Propagates all substitutions arriving at the PRIMARY slot if and only if (a matching substitution on the SECONDARY is
24 * present) xor (NEGATIVE).
25 *
26 * The negative parameter specifies whether this node checks for existence or non-existence.
27 * <p>
28 * It is mandatory in differential dataflow evaluation that the secondary parent is in an upstream dependency component
29 * (so that every secondary tuple comes with zero timestamp).
30 *
31 * @author Gabor Bergmann
32 */
33public class ExistenceNode extends DualInputNode {
34
35 protected boolean negative;
36
37 /**
38 * @param reteContainer
39 * @param negative
40 * if false, act as existence checker, otherwise a nonexistence-checker
41 */
42 public ExistenceNode(final ReteContainer reteContainer, final boolean negative) {
43 super(reteContainer, null);
44 this.negative = negative;
45 this.logic = createLogic();
46 }
47
48 @Override
49 public Tuple calibrate(final Tuple primary, final Tuple secondary) {
50 return primary;
51 }
52
53 @Override
54 public void networkStructureChanged() {
55 if (this.reteContainer.isTimelyEvaluation() && this.secondarySlot != null
56 && this.reteContainer.getCommunicationTracker().areInSameGroup(this, this.secondarySlot)) {
57 throw new IllegalStateException("Secondary parent must be in an upstream dependency component!");
58 }
59 super.networkStructureChanged();
60 }
61
62 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
63
64 @Override
65 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
66 throw new UnsupportedOperationException();
67 }
68
69 @Override
70 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
71 if (primarySlot == null || secondarySlot == null) {
72 return;
73 }
74 if (flush) {
75 reteContainer.flushUpdates();
76 }
77
78 for (final Tuple signature : primarySlot.getSignatures()) {
79 // primaries can not be null due to the contract of IterableIndex.getSignatures()
80 final Collection<Tuple> primaries = primarySlot.get(signature);
81 final Collection<Tuple> opposites = secondarySlot.get(signature);
82 if ((opposites != null) ^ negative) {
83 collector.addAll(primaries);
84 }
85 }
86 }
87
88 @Override
89 public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement,
90 final Tuple signature, final boolean change, final Timestamp timestamp) {
91 // in the default case, all timestamps must be zero
92 assert Timestamp.ZERO.equals(timestamp);
93
94 switch (side) {
95 case PRIMARY:
96 if ((retrieveOpposites(side, signature) != null) ^ negative) {
97 propagateUpdate(direction, updateElement, timestamp);
98 }
99 break;
100 case SECONDARY:
101 if (change) {
102 final Collection<Tuple> opposites = retrieveOpposites(side, signature);
103 if (opposites != null) {
104 for (final Tuple opposite : opposites) {
105 propagateUpdate((negative ? direction.opposite() : direction), opposite, timestamp);
106 }
107 }
108 }
109 break;
110 case BOTH:
111 // in case the slots coincide,
112 // negative --> always empty
113 // !positive --> identity
114 if (!negative) {
115 propagateUpdate(direction, updateElement, timestamp);
116 }
117 break;
118 }
119 }
120 };
121
122 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
123
124 @Override
125 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
126 if (primarySlot == null || secondarySlot == null) {
127 return;
128 }
129 if (flush) {
130 reteContainer.flushUpdates();
131 }
132
133 for (final Tuple signature : primarySlot.getSignatures()) {
134 // primaries can not be null due to the contract of IterableIndex.getSignatures()
135 final Map<Tuple, Timeline<Timestamp>> primaries = getTimeline(signature, primarySlot);
136 // see contract: secondary must be in an upstream SCC
137 final Collection<Tuple> opposites = secondarySlot.get(signature);
138 if ((opposites != null) ^ negative) {
139 for (final Tuple primary : primaries.keySet()) {
140 collector.put(primary, primaries.get(primary));
141 }
142 }
143 }
144 }
145
146 @Override
147 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
148 ExistenceNode.this.TIMELESS.pullInto(collector, flush);
149 }
150
151 @Override
152 public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement,
153 final Tuple signature, final boolean change, final Timestamp timestamp) {
154 switch (side) {
155 case PRIMARY: {
156 final Collection<Tuple> opposites = secondarySlot.get(signature);
157 if ((opposites != null) ^ negative) {
158 propagateUpdate(direction, updateElement, timestamp);
159 }
160 break;
161 }
162 case SECONDARY: {
163 final Map<Tuple, Timeline<Timestamp>> opposites = primarySlot.getTimeline(signature);
164 if (change) {
165 if (opposites != null) {
166 for (final Tuple opposite : opposites.keySet()) {
167 for (final Signed<Timestamp> oppositeSigned : opposites.get(opposite).asChangeSequence()) {
168 final Direction product = direction.multiply(oppositeSigned.getDirection());
169 propagateUpdate((negative ? product.opposite() : product), opposite,
170 oppositeSigned.getPayload());
171 }
172 }
173 }
174 }
175 break;
176 }
177 case BOTH:
178 // in case the slots coincide,
179 // negative --> always empty
180 // positive --> identity
181 if (!negative) {
182 propagateUpdate(direction, updateElement, timestamp);
183 }
184 break;
185 }
186 }
187 };
188
189 @Override
190 protected NetworkStructureChangeSensitiveLogic createTimelessLogic() {
191 return this.TIMELESS;
192 }
193
194 @Override
195 protected NetworkStructureChangeSensitiveLogic createTimelyLogic() {
196 return this.TIMELY;
197 }
198
199}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.Iterator;
14import java.util.Map;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
20import tools.refinery.viatra.runtime.rete.network.Receiver;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23
24/**
25 * A generic Indexer capable of indexing along any valid TupleMask. Does not keep track of parents, because will not
26 * ever pull parents.
27 *
28 * @author Gabor Bergmann
29 *
30 */
31public class GenericProjectionIndexer extends IndexerWithMemory implements ProjectionIndexer {
32
33 public GenericProjectionIndexer(ReteContainer reteContainer, TupleMask mask) {
34 super(reteContainer, mask);
35 }
36
37 @Override
38 protected void update(Direction direction, Tuple updateElement, Tuple signature, boolean change,
39 Timestamp timestamp) {
40 propagate(direction, updateElement, signature, change, timestamp);
41 }
42
43 @Override
44 public Collection<Tuple> get(Tuple signature) {
45 return memory.get(signature);
46 }
47
48 @Override
49 public Map<Tuple, Timeline<Timestamp>> getTimeline(Tuple signature) {
50 return memory.getWithTimeline(signature);
51 }
52
53 @Override
54 public Iterator<Tuple> iterator() {
55 return memory.iterator();
56 }
57
58 @Override
59 public Iterable<Tuple> getSignatures() {
60 return memory.getSignatures();
61 }
62
63 /**
64 * @since 2.0
65 */
66 @Override
67 public int getBucketCount() {
68 return memory.getKeysetSize();
69 }
70
71 @Override
72 public Receiver getActiveNode() {
73 return this;
74 }
75
76}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2012 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Iterator;
15import java.util.List;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.rete.network.Node;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.Supplier;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24
25/**
26 * Defines an abstract trivial indexer that identically projects the contents of some stateful node, and can therefore
27 * save space. Can only exist in connection with a stateful store, and must be operated by another node (the active
28 * node). Do not attach parents directly!
29 *
30 * @author Gabor Bergmann
31 * @noimplement Rely on the provided implementations
32 * @noreference Use only via standard Node and Indexer interfaces
33 * @noinstantiate This class is not intended to be instantiated by clients.
34 */
35public abstract class IdentityIndexer extends SpecializedProjectionIndexer {
36
37 protected abstract Collection<Tuple> getTuples();
38
39 public IdentityIndexer(ReteContainer reteContainer, int tupleWidth, Supplier parent,
40 Node activeNode, List<ListenerSubscription> sharedSubscriptionList) {
41 super(reteContainer, TupleMask.identity(tupleWidth), parent, activeNode, sharedSubscriptionList);
42 }
43
44 @Override
45 public Collection<Tuple> get(Tuple signature) {
46 if (contains(signature)) {
47 return Collections.singleton(signature);
48 } else
49 return null;
50 }
51
52 protected boolean contains(Tuple signature) {
53 return getTuples().contains(signature);
54 }
55
56 @Override
57 public Collection<Tuple> getSignatures() {
58 return getTuples();
59 }
60
61 @Override
62 public int getBucketCount() {
63 return getTuples().size();
64 }
65
66 @Override
67 public Iterator<Tuple> iterator() {
68 return getTuples().iterator();
69 }
70
71 @Override
72 public void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement, Timestamp timestamp) {
73 listener.notifyIndexerUpdate(direction, updateElement, updateElement, true, timestamp);
74 }
75
76} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.Node;
19import tools.refinery.viatra.runtime.rete.network.Supplier;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22/**
23 * A node that indexes incoming Tuples by their signatures as specified by a TupleMask. Notifies listeners about such
24 * update events through the IndexerListener.
25 *
26 * Signature tuples are created by transforming the update tuples using the mask. Tuples stored with the same signature
27 * are grouped together. The group or a reduction thereof is retrievable.
28 *
29 * @author Gabor Bergmann
30 */
31public interface Indexer extends Node {
32 /**
33 * @return the mask by which the contents are indexed.
34 */
35 public TupleMask getMask();
36
37 /**
38 * @return the node whose contents are indexed.
39 */
40 public Supplier getParent();
41
42 /**
43 * @return all stored tuples that conform to the specified signature, null if there are none such. CONTRACT: do not
44 * modify!
45 */
46 public Collection<Tuple> get(Tuple signature);
47
48 /**
49 * @since 2.4
50 */
51 default public Map<Tuple, Timeline<Timestamp>> getTimeline(Tuple signature) {
52 throw new UnsupportedOperationException();
53 }
54
55 /**
56 * This indexer will be updated whenever a Rete update is sent to the active node (or an equivalent time slot
57 * allotted to it). The active node is typically the indexer itself, but it can be a different node such as its
58 * parent.
59 *
60 * @return the active node that operates this indexer
61 */
62 public Node getActiveNode();
63
64
65 public Collection<IndexerListener> getListeners();
66
67 public void attachListener(IndexerListener listener);
68
69 public void detachListener(IndexerListener listener);
70
71}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.util.Direction;
14import tools.refinery.viatra.runtime.rete.network.Node;
15import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
16
17/**
18 * A listener for update events concerning an Indexer.
19 *
20 * @author Gabor Bergmann
21 *
22 */
23public interface IndexerListener {
24 /**
25 * Notifies recipient that the indexer has just received an update. Contract: indexer already reflects the updated
26 * state.
27 *
28 * @param direction
29 * the direction of the update.
30 * @param updateElement
31 * the tuple that was updated.
32 * @param signature
33 * the signature of the tuple according to the indexer's mask.
34 * @param change
35 * whether this was the first inserted / last revoked update element with this particular signature.
36 * @since 2.4
37 */
38 void notifyIndexerUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp);
39
40 Node getOwner();
41}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.matchers.util.Signed;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
25import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
26import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
27import tools.refinery.viatra.runtime.rete.network.Receiver;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.Supplier;
30import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
31import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
32import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
33import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
34import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
35import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
36
37/**
38 * @author Gabor Bergmann
39 * @author Tamas Szabo
40 */
41public abstract class IndexerWithMemory extends StandardIndexer
42 implements Receiver, NetworkStructureChangeSensitiveNode, ResumableNode {
43
44 protected MaskedTupleMemory<Timestamp> memory;
45
46 /**
47 * @since 2.3
48 */
49 protected NetworkStructureChangeSensitiveLogic logic;
50
51 /**
52 * @since 1.6
53 */
54 protected final Mailbox mailbox;
55
56 /**
57 * @since 2.4
58 */
59 protected CommunicationGroup group;
60
61 public IndexerWithMemory(final ReteContainer reteContainer, final TupleMask mask) {
62 super(reteContainer, mask);
63 final boolean isTimely = reteContainer.isTimelyEvaluation()
64 && reteContainer.getCommunicationTracker().isInRecursiveGroup(this);
65 memory = MaskedTupleMemory.create(mask, MemoryType.SETS, this, isTimely, isTimely && reteContainer
66 .getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL);
67 reteContainer.registerClearable(memory);
68 mailbox = instantiateMailbox();
69 reteContainer.registerClearable(mailbox);
70 this.logic = createLogic();
71 }
72
73 @Override
74 public CommunicationGroup getCurrentGroup() {
75 return this.group;
76 }
77
78 @Override
79 public void setCurrentGroup(final CommunicationGroup group) {
80 this.group = group;
81 }
82
83 @Override
84 public void networkStructureChanged() {
85 super.networkStructureChanged();
86 final boolean wasTimely = this.memory.isTimely();
87 final boolean isTimely = this.reteContainer.isTimelyEvaluation()
88 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this);
89 if (wasTimely != isTimely) {
90 final MaskedTupleMemory<Timestamp> newMemory = MaskedTupleMemory.create(mask, MemoryType.SETS, this,
91 isTimely, isTimely && reteContainer.getTimelyConfiguration()
92 .getTimelineRepresentation() == TimelineRepresentation.FAITHFUL);
93 newMemory.initializeWith(this.memory, Timestamp.ZERO);
94 memory.clear();
95 memory = newMemory;
96 }
97 this.logic = createLogic();
98 }
99
100 /**
101 * Instantiates the {@link Mailbox} of this receiver. Subclasses may override this method to provide their own
102 * mailbox implementation.
103 *
104 * @return the mailbox
105 * @since 2.0
106 */
107 protected Mailbox instantiateMailbox() {
108 if (this.reteContainer.isTimelyEvaluation()) {
109 return new TimelyMailbox(this, this.reteContainer);
110 } else {
111 return new BehaviorChangingMailbox(this, this.reteContainer);
112 }
113 }
114
115 @Override
116 public Mailbox getMailbox() {
117 return this.mailbox;
118 }
119
120 /**
121 * @since 2.0
122 */
123 public MaskedTupleMemory<Timestamp> getMemory() {
124 return memory;
125 }
126
127 @Override
128 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
129 this.logic.update(direction, updateElement, timestamp);
130 }
131
132 /**
133 * Refined version of update
134 *
135 * @since 2.4
136 */
137 protected abstract void update(final Direction direction, final Tuple updateElement, final Tuple signature,
138 final boolean change, final Timestamp timestamp);
139
140 @Override
141 public void appendParent(final Supplier supplier) {
142 if (parent == null) {
143 parent = supplier;
144 } else {
145 throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent
146 + ") and cannot connect to additional parent (" + supplier + "). ");
147 }
148 }
149
150 @Override
151 public void removeParent(final Supplier supplier) {
152 if (parent == supplier) {
153 parent = null;
154 } else {
155 throw new IllegalArgumentException(
156 "Illegal RETE edge removal: the parent of " + this + " is not " + supplier);
157 }
158 }
159
160 /**
161 * @since 2.4
162 */
163 @Override
164 public Collection<Supplier> getParents() {
165 return Collections.singleton(parent);
166 }
167
168 /**
169 * @since 2.4
170 */
171 @Override
172 public void resumeAt(final Timestamp timestamp) {
173 this.logic.resumeAt(timestamp);
174 }
175
176 /**
177 * @since 2.4
178 */
179 @Override
180 public Timestamp getResumableTimestamp() {
181 return this.memory.getResumableTimestamp();
182 }
183
184 /**
185 * @since 2.3
186 */
187 protected static abstract class NetworkStructureChangeSensitiveLogic {
188
189 /**
190 * @since 2.4
191 */
192 public abstract void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp);
193
194 /**
195 * @since 2.4
196 */
197 public abstract void resumeAt(final Timestamp timestamp);
198
199 }
200
201 /**
202 * @since 2.3
203 */
204 protected NetworkStructureChangeSensitiveLogic createLogic() {
205 if (this.reteContainer.isTimelyEvaluation()
206 && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
207 return TIMELY;
208 } else {
209 return TIMELESS;
210 }
211 }
212
213 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
214
215 @Override
216 public void resumeAt(final Timestamp timestamp) {
217 final Iterable<Tuple> signatures = memory.getResumableSignatures();
218
219 final Map<Tuple, Boolean> wasPresent = CollectionsFactory.createMap();
220 for (final Tuple signature : signatures) {
221 wasPresent.put(signature, memory.isPresentAtInfinity(signature));
222 }
223
224 final Map<Tuple, Map<Tuple, Diff<Timestamp>>> signatureMap = memory.resumeAt(timestamp);
225
226 for (final Entry<Tuple, Map<Tuple, Diff<Timestamp>>> outerEntry : signatureMap.entrySet()) {
227 final Tuple signature = outerEntry.getKey();
228 final Map<Tuple, Diff<Timestamp>> diffMap = outerEntry.getValue();
229 final boolean isPresent = memory.isPresentAtInfinity(signature);
230 // only send out a potential true value the first time for a given signature, then set it to false
231 boolean change = wasPresent.get(signature) ^ isPresent;
232
233 for (final Entry<Tuple, Diff<Timestamp>> innerEntry : diffMap.entrySet()) {
234 final Tuple tuple = innerEntry.getKey();
235 final Diff<Timestamp> diffs = innerEntry.getValue();
236 for (final Signed<Timestamp> signed : diffs) {
237 IndexerWithMemory.this.update(signed.getDirection(), tuple, signature, change,
238 signed.getPayload());
239 }
240 // change is a signature-wise flag, so it is ok to "try" to signal it for the first tuple only
241 change = false;
242 }
243 }
244
245 final Timestamp nextTimestamp = memory.getResumableTimestamp();
246 if (nextTimestamp != null) {
247 group.notifyHasMessage(mailbox, nextTimestamp);
248 }
249 }
250
251 @Override
252 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
253 final Tuple signature = mask.transform(update);
254 final boolean wasPresent = memory.isPresentAtInfinity(signature);
255 final Diff<Timestamp> resultDiff = direction == Direction.INSERT
256 ? memory.addWithTimestamp(update, signature, timestamp)
257 : memory.removeWithTimestamp(update, signature, timestamp);
258 final boolean isPresent = memory.isPresentAtInfinity(signature);
259 final boolean change = wasPresent ^ isPresent;
260 for (final Signed<Timestamp> signed : resultDiff) {
261 IndexerWithMemory.this.update(signed.getDirection(), update, signature, change, signed.getPayload());
262 }
263 }
264
265 };
266
267 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
268
269 @Override
270 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
271 final Tuple signature = mask.transform(update);
272 final boolean change = direction == Direction.INSERT ? memory.add(update, signature)
273 : memory.remove(update, signature);
274 IndexerWithMemory.this.update(direction, update, signature, change, timestamp);
275 }
276
277 @Override
278 public void resumeAt(final Timestamp timestamp) {
279 // there is nothing to resume in the timeless case because we do not even care about timestamps
280 }
281
282 };
283
284} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13
14/**
15 * An indexer that allows the iteration of all retrievable tuple groups (or reduced groups).
16 *
17 * @author Gabor Bergmann
18 *
19 */
20public interface IterableIndexer extends Indexer, Iterable<Tuple> {
21
22 /**
23 * A view consisting of exactly those signatures whose tuple group is not empty
24 * @since 2.0
25 */
26 public Iterable<Tuple> getSignatures();
27
28 /**
29 * @return the number of signatures whose tuple group is not empty
30 * @since 2.0
31 */
32 public int getBucketCount();
33
34}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.matchers.util.Signed;
19import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
20import tools.refinery.viatra.runtime.rete.network.ReteContainer;
21import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
22
23/**
24 * @author Gabor Bergmann
25 *
26 */
27public class JoinNode extends DualInputNode {
28
29 public JoinNode(final ReteContainer reteContainer, final TupleMask complementerSecondaryMask) {
30 super(reteContainer, complementerSecondaryMask);
31 this.logic = createLogic();
32 }
33
34 @Override
35 public Tuple calibrate(final Tuple primary, final Tuple secondary) {
36 return unify(primary, secondary);
37 }
38
39 private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() {
40
41 @Override
42 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
43 throw new UnsupportedOperationException();
44 }
45
46 @Override
47 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
48 if (primarySlot == null || secondarySlot == null) {
49 return;
50 }
51
52 if (flush) {
53 reteContainer.flushUpdates();
54 }
55
56 for (final Tuple signature : primarySlot.getSignatures()) {
57 // primaries can not be null due to the contract of IterableIndex.getSignatures()
58 final Collection<Tuple> primaries = primarySlot.get(signature);
59 final Collection<Tuple> opposites = secondarySlot.get(signature);
60 if (opposites != null) {
61 for (final Tuple primary : primaries) {
62 for (final Tuple opposite : opposites) {
63 collector.add(unify(primary, opposite));
64 }
65 }
66 }
67 }
68 }
69
70 @Override
71 public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement,
72 final Tuple signature, final boolean change, final Timestamp timestamp) {
73 // in the default case, all timestamps must be zero
74 assert Timestamp.ZERO.equals(timestamp);
75
76 final Collection<Tuple> opposites = retrieveOpposites(side, signature);
77
78 if (!coincidence) {
79 if (opposites != null) {
80 for (final Tuple opposite : opposites) {
81 propagateUpdate(direction, unify(side, updateElement, opposite), timestamp);
82 }
83 }
84 } else {
85 // compensate for coincidence of slots - this is the case when an Indexer is joined with itself
86 if (opposites != null) {
87 for (final Tuple opposite : opposites) {
88 if (opposite.equals(updateElement)) {
89 // handle self-joins of a single tuple separately
90 continue;
91 }
92 propagateUpdate(direction, unify(opposite, updateElement), timestamp);
93 propagateUpdate(direction, unify(updateElement, opposite), timestamp);
94 }
95 }
96
97 // handle self-joins here
98 propagateUpdate(direction, unify(updateElement, updateElement), timestamp);
99 }
100 }
101 };
102
103 private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() {
104
105 @Override
106 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
107 if (primarySlot == null || secondarySlot == null) {
108 return;
109 }
110
111 if (flush) {
112 reteContainer.flushUpdates();
113 }
114
115 for (final Tuple signature : primarySlot.getSignatures()) {
116 // primaries can not be null due to the contract of IterableIndex.getSignatures()
117 final Map<Tuple, Timeline<Timestamp>> primaries = getTimeline(signature, primarySlot);
118 final Map<Tuple, Timeline<Timestamp>> opposites = getTimeline(signature, secondarySlot);
119 if (opposites != null) {
120 for (final Tuple primary : primaries.keySet()) {
121 for (final Tuple opposite : opposites.keySet()) {
122 final Timeline<Timestamp> primaryTimeline = primaries.get(primary);
123 final Timeline<Timestamp> oppositeTimeline = opposites.get(opposite);
124 final Timeline<Timestamp> mergedTimeline = primaryTimeline
125 .mergeMultiplicative(oppositeTimeline);
126 if (!mergedTimeline.isEmpty()) {
127 collector.put(unify(primary, opposite), mergedTimeline);
128 }
129 }
130 }
131 }
132 }
133 }
134
135 @Override
136 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
137 JoinNode.this.TIMELESS.pullInto(collector, flush);
138 }
139
140 @Override
141 public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement,
142 final Tuple signature, final boolean change, final Timestamp timestamp) {
143 final Indexer oppositeIndexer = getSlot(side.opposite());
144 final Map<Tuple, Timeline<Timestamp>> opposites = getTimeline(signature, oppositeIndexer);
145
146 if (!coincidence) {
147 if (opposites != null) {
148 for (final Tuple opposite : opposites.keySet()) {
149 final Tuple unifiedTuple = unify(side, updateElement, opposite);
150 for (final Signed<Timestamp> signed : opposites.get(opposite).asChangeSequence()) {
151 // TODO only consider signed timestamps that are greater or equal to timestamp
152 // plus compact the previous timestamps into at most one update
153 propagateUpdate(signed.getDirection().multiply(direction), unifiedTuple,
154 timestamp.max(signed.getPayload()));
155 }
156 }
157 }
158 } else {
159 // compensate for coincidence of slots - this is the case when an Indexer is joined with itself
160 if (opposites != null) {
161 for (final Tuple opposite : opposites.keySet()) {
162 if (opposite.equals(updateElement)) {
163 // handle self-joins of a single tuple separately
164 continue;
165 }
166 final Tuple u1 = unify(opposite, updateElement);
167 final Tuple u2 = unify(updateElement, opposite);
168 for (final Signed<Timestamp> oppositeSigned : opposites.get(opposite).asChangeSequence()) {
169 final Direction updateDirection = direction.multiply(oppositeSigned.getDirection());
170 final Timestamp updateTimestamp = timestamp.max(oppositeSigned.getPayload());
171 propagateUpdate(updateDirection, u1, updateTimestamp);
172 propagateUpdate(updateDirection, u2, updateTimestamp);
173 }
174 }
175 }
176
177 // handle self-join here
178 propagateUpdate(direction, unify(updateElement, updateElement), timestamp);
179 }
180 }
181 };
182
183 @Override
184 protected NetworkStructureChangeSensitiveLogic createTimelessLogic() {
185 return this.TIMELESS;
186 }
187
188 @Override
189 protected NetworkStructureChangeSensitiveLogic createTimelyLogic() {
190 return this.TIMELY;
191 }
192
193}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.List;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.rete.network.Receiver;
17import tools.refinery.viatra.runtime.rete.network.ReteContainer;
18import tools.refinery.viatra.runtime.rete.network.Supplier;
19
20/**
21 * Defines a trivial indexer that identically projects the contents of a memory-equipped node, and can therefore save
22 * space. Can only exist in connection with a memory, and must be operated by another node. Do not attach parents
23 * directly!
24 *
25 * @noimplement Rely on the provided implementations
26 * @noreference Use only via standard Node and Indexer interfaces
27 * @noinstantiate This class is not intended to be instantiated by clients.
28 * @author Gabor Bergmann
29 */
30
31public class MemoryIdentityIndexer extends IdentityIndexer {
32
33 protected final Collection<Tuple> memory;
34
35 /**
36 * @param reteContainer
37 * @param tupleWidth
38 * the width of the tuples of memoryNode
39 * @param memory
40 * the memory whose contents are to be identity-indexed
41 * @param parent
42 * the parent node that owns the memory
43 */
44 public MemoryIdentityIndexer(ReteContainer reteContainer, int tupleWidth, Collection<Tuple> memory,
45 Supplier parent, Receiver activeNode, List<ListenerSubscription> sharedSubscriptionList) {
46 super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList);
47 this.memory = memory;
48 }
49
50 @Override
51 protected Collection<Tuple> getTuples() {
52 return this.memory;
53 }
54
55}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.List;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.rete.network.Receiver;
17import tools.refinery.viatra.runtime.rete.network.ReteContainer;
18import tools.refinery.viatra.runtime.rete.network.Supplier;
19
20/**
21 * Defines a trivial indexer that projects the contents of a memory-equipped node to the empty tuple, and can therefore
22 * save space. Can only exist in connection with a memory, and must be operated by another node. Do not attach parents
23 * directly!
24 *
25 * @author Gabor Bergmann
26 * @noimplement Rely on the provided implementations
27 * @noreference Use only via standard Node and Indexer interfaces
28 * @noinstantiate This class is not intended to be instantiated by clients.
29 */
30public class MemoryNullIndexer extends NullIndexer {
31
32 Collection<Tuple> memory;
33
34 /**
35 * @param reteContainer
36 * @param tupleWidth
37 * the width of the tuples of memoryNode
38 * @param memory
39 * the memory whose contents are to be null-indexed
40 * @param parent
41 * the parent node that owns the memory
42 */
43 public MemoryNullIndexer(ReteContainer reteContainer, int tupleWidth, Collection<Tuple> memory,
44 Supplier parent, Receiver activeNode, List<ListenerSubscription> sharedSubscriptionList) {
45 super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList);
46 this.memory = memory;
47 }
48
49 @Override
50 protected Collection<Tuple> getTuples() {
51 return this.memory;
52 }
53
54}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2012 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Iterator;
15import java.util.List;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
20import tools.refinery.viatra.runtime.matchers.util.Direction;
21import tools.refinery.viatra.runtime.rete.network.Node;
22import tools.refinery.viatra.runtime.rete.network.ReteContainer;
23import tools.refinery.viatra.runtime.rete.network.Supplier;
24import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
25
26/**
27 * Defines an abstract trivial indexer that projects the contents of some stateful node to the empty tuple, and can
28 * therefore save space. Can only exist in connection with a stateful store, and must be operated by another node (the
29 * active node). Do not attach parents directly!
30 *
31 * @author Gabor Bergmann
32 * @noimplement Rely on the provided implementations
33 * @noreference Use only via standard Node and Indexer interfaces
34 * @noinstantiate This class is not intended to be instantiated by clients.
35 */
36public abstract class NullIndexer extends SpecializedProjectionIndexer {
37
38 protected abstract Collection<Tuple> getTuples();
39
40 protected static final Tuple nullSignature = Tuples.staticArityFlatTupleOf();
41 protected static final Collection<Tuple> nullSingleton = Collections.singleton(nullSignature);
42 protected static final Collection<Tuple> emptySet = Collections.emptySet();
43
44 public NullIndexer(ReteContainer reteContainer, int tupleWidth, Supplier parent, Node activeNode,
45 List<ListenerSubscription> sharedSubscriptionList) {
46 super(reteContainer, TupleMask.linear(0, tupleWidth), parent, activeNode, sharedSubscriptionList);
47 }
48
49 @Override
50 public Collection<Tuple> get(Tuple signature) {
51 if (nullSignature.equals(signature))
52 return isEmpty() ? null : getTuples();
53 else
54 return null;
55 }
56
57 @Override
58 public Collection<Tuple> getSignatures() {
59 return isEmpty() ? emptySet : nullSingleton;
60 }
61
62 protected boolean isEmpty() {
63 return getTuples().isEmpty();
64 }
65
66 protected boolean isSingleElement() {
67 return getTuples().size() == 1;
68 }
69
70 @Override
71 public Iterator<Tuple> iterator() {
72 return getTuples().iterator();
73 }
74
75 @Override
76 public int getBucketCount() {
77 return getTuples().isEmpty() ? 0 : 1;
78 }
79
80 @Override
81 public void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement,
82 Timestamp timestamp) {
83 boolean radical = (direction == Direction.DELETE && isEmpty())
84 || (direction == Direction.INSERT && isSingleElement());
85 listener.notifyIndexerUpdate(direction, updateElement, nullSignature, radical, timestamp);
86 }
87
88} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
16import tools.refinery.viatra.runtime.rete.network.ReteContainer;
17import tools.refinery.viatra.runtime.rete.network.Supplier;
18
19/**
20 * @author Gabor Bergmann Indexer whose lifetime last until the first get() DO NOT connect to nodes!
21 */
22public class OnetimeIndexer extends GenericProjectionIndexer {
23
24 public OnetimeIndexer(ReteContainer reteContainer, TupleMask mask) {
25 super(reteContainer, mask);
26 }
27
28 @Override
29 public Collection<Tuple> get(Tuple signature) {
30 if (tools.refinery.viatra.runtime.rete.util.Options.releaseOnetimeIndexers) {
31 reteContainer.unregisterClearable(memory);
32 reteContainer.unregisterNode(this);
33 }
34 return super.get(signature);
35 }
36
37 @Override
38 public void appendParent(Supplier supplier) {
39 throw new UnsupportedOperationException("onetime indexer cannot have parents");
40 }
41
42 @Override
43 public void attachListener(IndexerListener listener) {
44 throw new UnsupportedOperationException("onetime indexer cannot have listeners");
45 }
46
47}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12/**
13 * An iterable indexer that receives updates from a node, and groups received tuples intact, i.e. it does not reduce
14 * tuple groups.
15 *
16 * @author Gabor Bergmann
17 *
18 */
19public interface ProjectionIndexer extends IterableIndexer {
20
21}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2012 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.ArrayList;
13import java.util.List;
14import java.util.Objects;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.rete.network.Node;
20import tools.refinery.viatra.runtime.rete.network.ReteContainer;
21import tools.refinery.viatra.runtime.rete.network.Supplier;
22import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24
25/**
26 * A specialized projection indexer that can be memory-less (relying on an external source of information).
27 *
28 * <p>
29 * All specialized projection indexers of a single node will share the same listener list, so that notification order is
30 * maintained (see Bug 518434).
31 *
32 * @author Gabor Bergmann
33 * @noimplement Rely on the provided implementations
34 * @noreference Use only via standard Node and Indexer interfaces
35 * @noinstantiate This class is not intended to be instantiated by clients.
36 */
37public abstract class SpecializedProjectionIndexer extends StandardIndexer implements ProjectionIndexer {
38
39 protected Node activeNode;
40 protected List<ListenerSubscription> subscriptions;
41
42 /**
43 * @since 1.7
44 */
45 public SpecializedProjectionIndexer(final ReteContainer reteContainer, final TupleMask mask, final Supplier parent,
46 final Node activeNode, final List<ListenerSubscription> subscriptions) {
47 super(reteContainer, mask);
48 this.parent = parent;
49 this.activeNode = activeNode;
50 this.subscriptions = subscriptions;
51 }
52
53 public List<ListenerSubscription> getSubscriptions() {
54 return subscriptions;
55 }
56
57 @Override
58 public Node getActiveNode() {
59 return activeNode;
60 }
61
62 @Override
63 protected void propagate(final Direction direction, final Tuple updateElement, final Tuple signature,
64 final boolean change, final Timestamp timestamp) {
65 throw new UnsupportedOperationException();
66 }
67
68 @Override
69 public void attachListener(final IndexerListener listener) {
70 super.attachListener(listener);
71 final CommunicationTracker tracker = this.getCommunicationTracker();
72 final IndexerListener proxy = tracker.proxifyIndexerListener(this, listener);
73 final ListenerSubscription subscription = new ListenerSubscription(this, proxy);
74 tracker.registerDependency(this, proxy.getOwner());
75 // See Bug 518434
76 // Must add to the first position, so that the later listeners are notified earlier.
77 // Thus if the beta node added as listener is also an indirect descendant of the same indexer on its opposite
78 // slot,
79 // then the beta node is connected later than its ancestor's listener, therefore it will be notified earlier,
80 // eliminating duplicate insertions and lost deletions that would result from fall-through update propagation
81 subscriptions.add(0, subscription);
82 }
83
84 @Override
85 public void detachListener(final IndexerListener listener) {
86 final CommunicationTracker tracker = this.getCommunicationTracker();
87 // obtain the proxy before the super call would unregister the dependency
88 final IndexerListener proxy = tracker.proxifyIndexerListener(this, listener);
89 super.detachListener(listener);
90 final ListenerSubscription subscription = new ListenerSubscription(this, proxy);
91 final boolean wasContained = subscriptions.remove(subscription);
92 assert wasContained;
93 tracker.unregisterDependency(this, proxy.getOwner());
94 }
95
96 @Override
97 public void networkStructureChanged() {
98 super.networkStructureChanged();
99 final List<ListenerSubscription> oldSubscriptions = new ArrayList<ListenerSubscription>();
100 oldSubscriptions.addAll(subscriptions);
101 subscriptions.clear();
102 for (final ListenerSubscription oldSubscription : oldSubscriptions) {
103 // there is no need to unregister and re-register the dependency between indexer and listener
104 // because the owner of the listener is the same (even if it is proxified)
105 final CommunicationTracker tracker = this.getCommunicationTracker();
106 // the subscriptions are shared, so we MUST reuse the indexer of the subscription instead of simply 'this'
107 final IndexerListener proxy = tracker.proxifyIndexerListener(oldSubscription.indexer, oldSubscription.listener);
108 final ListenerSubscription newSubscription = new ListenerSubscription(oldSubscription.indexer, proxy);
109 subscriptions.add(newSubscription);
110 }
111 }
112
113 /**
114 * @since 2.4
115 */
116 public abstract void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement,
117 Timestamp timestamp);
118
119 /**
120 * Infrastructure to share subscriptions between specialized indexers of the same parent node.
121 *
122 * @author Gabor Bergmann
123 * @since 1.7
124 */
125 public static class ListenerSubscription {
126 protected SpecializedProjectionIndexer indexer;
127 protected IndexerListener listener;
128
129 public ListenerSubscription(SpecializedProjectionIndexer indexer, IndexerListener listener) {
130 super();
131 this.indexer = indexer;
132 this.listener = listener;
133 }
134
135 /**
136 * @since 2.4
137 */
138 public SpecializedProjectionIndexer getIndexer() {
139 return indexer;
140 }
141
142 /**
143 * @since 2.4
144 */
145 public IndexerListener getListener() {
146 return listener;
147 }
148
149 /**
150 * Call this from parent node.
151 * @since 2.4
152 */
153 public void propagate(Direction direction, Tuple updateElement, Timestamp timestamp) {
154 indexer.propagateToListener(listener, direction, updateElement, timestamp);
155 }
156
157 @Override
158 public int hashCode() {
159 return Objects.hash(indexer, listener);
160 }
161
162 @Override
163 public boolean equals(Object obj) {
164 if (this == obj)
165 return true;
166 if (obj == null)
167 return false;
168 if (getClass() != obj.getClass())
169 return false;
170 ListenerSubscription other = (ListenerSubscription) obj;
171 return Objects.equals(listener, other.listener) && Objects.equals(indexer, other.indexer);
172 }
173
174 }
175
176}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.index;
11
12import java.util.Collection;
13import java.util.List;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.rete.network.BaseNode;
20import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.Supplier;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
25
26/**
27 * An abstract standard implementation of the Indexer interface, providing common bookkeeping functionality.
28 *
29 * @author Gabor Bergmann
30 *
31 */
32public abstract class StandardIndexer extends BaseNode implements Indexer, NetworkStructureChangeSensitiveNode {
33
34 protected Supplier parent;
35 private final List<IndexerListener> originalListeners;
36 private final List<IndexerListener> proxyListeners;
37 protected TupleMask mask;
38
39 public StandardIndexer(ReteContainer reteContainer, TupleMask mask) {
40 super(reteContainer);
41 this.parent = null;
42 this.mask = mask;
43 this.originalListeners = CollectionsFactory.createObserverList();
44 this.proxyListeners = CollectionsFactory.createObserverList();
45 }
46
47 /**
48 * @since 2.4
49 */
50 protected void propagate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp) {
51 for (IndexerListener listener : proxyListeners) {
52 listener.notifyIndexerUpdate(direction, updateElement, signature, change, timestamp);
53 }
54 }
55
56 @Override
57 public TupleMask getMask() {
58 return mask;
59 }
60
61 @Override
62 public Supplier getParent() {
63 return parent;
64 }
65
66 @Override
67 public void attachListener(IndexerListener listener) {
68 this.getCommunicationTracker().registerDependency(this, listener.getOwner());
69 // obtain the proxy after registering the dependency because then the proxy reflects the new SCC structure
70 final IndexerListener proxy = this.getCommunicationTracker().proxifyIndexerListener(this, listener);
71 // See Bug 518434
72 // Must add to the first position, so that the later listeners are notified earlier.
73 // Thus if the beta node added as listener is also an indirect descendant of the same indexer on its opposite slot,
74 // then the beta node is connected later than its ancestor's listener, therefore it will be notified earlier,
75 // eliminating duplicate insertions and lost deletions that would result from fall-through update propagation
76 this.originalListeners.add(0, listener);
77 this.proxyListeners.add(0, proxy);
78 }
79
80 @Override
81 public void detachListener(IndexerListener listener) {
82 this.originalListeners.remove(listener);
83 IndexerListener listenerToRemove = null;
84 for (final IndexerListener proxyListener : this.proxyListeners) {
85 if (proxyListener.getOwner() == listener.getOwner()) {
86 listenerToRemove = proxyListener;
87 break;
88 }
89 }
90 assert listenerToRemove != null;
91 this.proxyListeners.remove(listenerToRemove);
92 this.getCommunicationTracker().unregisterDependency(this, listener.getOwner());
93 }
94
95 @Override
96 public void networkStructureChanged() {
97 this.proxyListeners.clear();
98 for (final IndexerListener original : this.originalListeners) {
99 this.proxyListeners.add(this.getCommunicationTracker().proxifyIndexerListener(this, original));
100 }
101 }
102
103 @Override
104 public Collection<IndexerListener> getListeners() {
105 return proxyListeners;
106 }
107
108 @Override
109 public ReteContainer getContainer() {
110 return reteContainer;
111 }
112
113 @Override
114 protected String toStringCore() {
115 return super.toStringCore() + "(" + parent + "/" + mask + ")";
116 }
117
118 @Override
119 public void assignTraceInfo(TraceInfo traceInfo) {
120 super.assignTraceInfo(traceInfo);
121 if (traceInfo.propagateFromIndexerToSupplierParent())
122 if (parent != null)
123 parent.acceptPropagatedTraceInfo(traceInfo);
124 }
125
126
127}
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..77202004
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java
@@ -0,0 +1,121 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.index;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.rete.itc.alg.incscc.IncSCCAlg;
17import tools.refinery.viatra.runtime.matchers.tuple.MaskedTuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
21import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.rete.network.Receiver;
24import tools.refinery.viatra.runtime.rete.single.TransitiveClosureNode;
25
26// UNFINISHED, not used yet
27public class TransitiveClosureNodeIndexer extends StandardIndexer implements IterableIndexer {
28 private TransitiveClosureNode tcNode;
29 private IncSCCAlg<Object> tcAlg;
30 private Collection<Tuple> emptySet;
31
32 public TransitiveClosureNodeIndexer(TupleMask mask, IncSCCAlg<Object> tcAlg, TransitiveClosureNode tcNode) {
33 super(tcNode.getContainer(), mask);
34 this.tcAlg = tcAlg;
35 this.tcNode = tcNode;
36 this.emptySet = Collections.emptySet();
37 this.parent = tcNode;
38 }
39
40 @Override
41 public Collection<Tuple> get(Tuple signature) {
42 if (signature.getSize() == mask.sourceWidth) {
43 if (mask.indices.length == 0) {
44 // mask ()/2
45 return getSignatures();
46 } else if (mask.indices.length == 1) {
47 Set<Tuple> retSet = CollectionsFactory.createSet();
48
49 // mask (0)/2
50 if (mask.indices[0] == 0) {
51 Object source = signature.get(0);
52 for (Object target : tcAlg.getAllReachableTargets(source)) {
53 retSet.add(Tuples.staticArityFlatTupleOf(source, target));
54 }
55 return retSet;
56 }
57 // mask (1)/2
58 if (mask.indices[0] == 1) {
59 Object target = signature.get(1);
60 for (Object source : tcAlg.getAllReachableSources(target)) {
61 retSet.add(Tuples.staticArityFlatTupleOf(source, target));
62 }
63 return retSet;
64 }
65 } else {
66 // mask (0,1)/2
67 if (mask.indices[0] == 0 && mask.indices[1] == 1) {
68 Object source = signature.get(0);
69 Object target = signature.get(1);
70 Tuple singleton = Tuples.staticArityFlatTupleOf(source, target);
71 return (tcAlg.isReachable(source, target) ? Collections.singleton(singleton) : emptySet);
72 }
73 // mask (1,0)/2
74 if (mask.indices[0] == 1 && mask.indices[1] == 0) {
75 Object source = signature.get(1);
76 Object target = signature.get(0);
77 Tuple singleton = Tuples.staticArityFlatTupleOf(source, target);
78 return (tcAlg.isReachable(source, target) ? Collections.singleton(singleton) : emptySet);
79 }
80 }
81 }
82 return null;
83 }
84
85 @Override
86 public int getBucketCount() {
87 throw new UnsupportedOperationException();
88 }
89
90 @Override
91 public Collection<Tuple> getSignatures() {
92 return asTupleCollection(tcAlg.getTcRelation());
93 }
94
95 @Override
96 public Iterator<Tuple> iterator() {
97 return asTupleCollection(tcAlg.getTcRelation()).iterator();
98 }
99
100 private Collection<Tuple> asTupleCollection(
101 Collection<tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple<Object>> tuples) {
102 Set<Tuple> retSet = CollectionsFactory.createSet();
103 for (tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple<Object> tuple : tuples) {
104 retSet.add(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget()));
105 }
106 return retSet;
107 }
108
109 /**
110 * @since 2.4
111 */
112 public void propagate(Direction direction, Tuple updateElement, boolean change) {
113 propagate(direction, updateElement, new MaskedTuple(updateElement, mask), change, null);
114 }
115
116 @Override
117 public Receiver getActiveNode() {
118 return tcNode;
119 }
120
121}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.index.timely;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.List;
14import java.util.Map;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.index.IdentityIndexer;
20import tools.refinery.viatra.runtime.rete.network.Receiver;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.Supplier;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24
25public class TimelyMemoryIdentityIndexer extends IdentityIndexer {
26
27 protected final TimelyMemory<Timestamp> memory;
28
29 public TimelyMemoryIdentityIndexer(final ReteContainer reteContainer, final int tupleWidth,
30 final TimelyMemory<Timestamp> memory, final Supplier parent, final Receiver activeNode,
31 final List<ListenerSubscription> sharedSubscriptionList) {
32 super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList);
33 this.memory = memory;
34 }
35
36 @Override
37 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
38 final Timeline<Timestamp> timestamp = this.memory.get(signature);
39 if (timestamp != null) {
40 return Collections.singletonMap(signature, timestamp);
41 } else {
42 return null;
43 }
44 }
45
46 @Override
47 protected Collection<Tuple> getTuples() {
48 return this.memory.getTuplesAtInfinity();
49 }
50
51}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.index.timely;
10
11import java.util.Collection;
12import java.util.List;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.index.NullIndexer;
19import tools.refinery.viatra.runtime.rete.network.Receiver;
20import tools.refinery.viatra.runtime.rete.network.ReteContainer;
21import tools.refinery.viatra.runtime.rete.network.Supplier;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23
24public class TimelyMemoryNullIndexer extends NullIndexer {
25
26 protected final TimelyMemory<Timestamp> memory;
27
28 public TimelyMemoryNullIndexer(final ReteContainer reteContainer, final int tupleWidth,
29 final TimelyMemory<Timestamp> memory, final Supplier parent,
30 final Receiver activeNode, final List<ListenerSubscription> sharedSubscriptionList) {
31 super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList);
32 this.memory = memory;
33 }
34
35 @Override
36 public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) {
37 if (nullSignature.equals(signature)) {
38 return isEmpty() ? null : this.memory.asMap();
39 } else {
40 return null;
41 }
42 }
43
44 @Override
45 protected Collection<Tuple> getTuples() {
46 return this.memory.getTuplesAtInfinity();
47 }
48
49}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.counting;
11
12import java.util.List;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.rete.itc.alg.misc.DFSPathFinder;
16import tools.refinery.viatra.runtime.rete.itc.alg.misc.IGraphPathFinder;
17import tools.refinery.viatra.runtime.rete.itc.alg.misc.ITcRelation;
18import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource;
19import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalWrapper;
20import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource;
21import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphObserver;
22import tools.refinery.viatra.runtime.rete.itc.igraph.ITcDataSource;
23import tools.refinery.viatra.runtime.rete.itc.igraph.ITcObserver;
24import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
25import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
26
27/**
28 * This class is the optimized implementation of the Counting algorithm.
29 *
30 * @author Tamas Szabo
31 *
32 * @param <V>
33 * the type parameter of the nodes in the graph data source
34 */
35public class CountingAlg<V> implements IGraphObserver<V>, ITcDataSource<V> {
36
37 private CountingTcRelation<V> tc = null;
38 private IBiDirectionalGraphDataSource<V> gds = null;
39 private List<ITcObserver<V>> observers;
40
41 /**
42 * Constructs a new Counting algorithm and initializes the transitive closure relation with the given graph data
43 * source. Attach itself on the graph data source as an observer.
44 *
45 * @param gds
46 * the graph data source instance
47 */
48 public CountingAlg(IGraphDataSource<V> gds) {
49
50 if (gds instanceof IBiDirectionalGraphDataSource<?>) {
51 this.gds = (IBiDirectionalGraphDataSource<V>) gds;
52 } else {
53 this.gds = new IBiDirectionalWrapper<V>(gds);
54 }
55
56 observers = CollectionsFactory.<ITcObserver<V>>createObserverList();
57 tc = new CountingTcRelation<V>(true);
58
59 initTc();
60 gds.attachObserver(this);
61 }
62
63 /**
64 * Initializes the transitive closure relation.
65 */
66 private void initTc() {
67 this.setTcRelation(CountingTcRelation.createFrom(gds));
68 }
69
70 @Override
71 public void edgeInserted(V source, V target) {
72 if (!source.equals(target)) {
73 deriveTc(source, target, true);
74 }
75 }
76
77 @Override
78 public void edgeDeleted(V source, V target) {
79 if (!source.equals(target)) {
80 deriveTc(source, target, false);
81 }
82 }
83
84 @Override
85 public void nodeInserted(V n) {
86
87 }
88
89 @Override
90 public void nodeDeleted(V n) {
91 this.tc.deleteTupleEnd(n);
92 }
93
94 /**
95 * Derives the transitive closure relation when an edge is inserted or deleted.
96 *
97 * @param source
98 * the source of the edge
99 * @param target
100 * the target of the edge
101 * @param dCount
102 * the value is -1 if an edge was deleted and +1 if an edge was inserted
103 */
104 private void deriveTc(V source, V target, boolean isInsertion) {
105
106 // if (dCount == 1 && isReachable(target, source)) {
107 // System.out.println("The graph contains cycle with (" + source + ","+ target + ") edge!");
108 // }
109
110 CountingTcRelation<V> dtc = new CountingTcRelation<V>(false);
111 Set<V> tupEnds = null;
112
113 // 1. d(tc(x,y)) :- d(l(x,y))
114 if (tc.updateTuple(source, target, isInsertion)) {
115 dtc.updateTuple(source, target, true /* deltas implicitly have the same sign as isInsertion*/);
116 notifyTcObservers(source, target, isInsertion);
117 }
118
119 // 2. d(tc(x,y)) :- d(l(x,z)) & tc(z,y)
120 tupEnds = tc.getTupleEnds(target);
121 if (tupEnds != null) {
122 for (V tupEnd : tupEnds) {
123 if (!tupEnd.equals(source)) {
124 if (tc.updateTuple(source, tupEnd, isInsertion)) {
125 dtc.updateTuple(source, tupEnd, true /* deltas implicitly have the same sign as isInsertion*/);
126 notifyTcObservers(source, tupEnd, isInsertion);
127 }
128 }
129 }
130 }
131
132 // 3. d(tc(x,y)) :- lv(x,z) & d(tc(z,y))
133 CountingTcRelation<V> newTuples = dtc;
134 CountingTcRelation<V> tmp = null;
135 dtc = new CountingTcRelation<V>(false);
136
137 IMemoryView<V> nodes = null;
138
139 while (!newTuples.isEmpty()) {
140
141 tmp = dtc;
142 dtc = newTuples;
143 newTuples = tmp;
144 newTuples.clear();
145
146 for (V tS : dtc.getTupleStarts()) {
147 nodes = gds.getSourceNodes(tS);
148 for (V nS : nodes.distinctValues()) {
149 int count = nodes.getCount(nS);
150 for (int i = 0; i < count; i++) {
151 tupEnds = dtc.getTupleEnds(tS);
152 if (tupEnds != null) {
153 for (V tT : tupEnds) {
154 if (!nS.equals(tT)) {
155 if (tc.updateTuple(nS, tT, isInsertion)) {
156 newTuples.updateTuple(nS, tT, true /* deltas implicitly have the same sign as isInsertion*/);
157 notifyTcObservers(nS, tT, isInsertion);
158 }
159 }
160 }
161 }
162 }
163 }
164 }
165 }
166
167 // System.out.println(tc);
168 }
169
170 public ITcRelation<V> getTcRelation() {
171 return this.tc;
172 }
173
174 public void setTcRelation(CountingTcRelation<V> tc) {
175 this.tc = tc;
176 }
177
178 @Override
179 public boolean isReachable(V source, V target) {
180 return tc.containsTuple(source, target);
181 }
182
183 @Override
184 public void attachObserver(ITcObserver<V> to) {
185 this.observers.add(to);
186
187 }
188
189 @Override
190 public void detachObserver(ITcObserver<V> to) {
191 this.observers.remove(to);
192 }
193
194 @Override
195 public Set<V> getAllReachableTargets(V source) {
196 return tc.getTupleEnds(source);
197 }
198
199 @Override
200 public Set<V> getAllReachableSources(V target) {
201 return tc.getTupleStarts(target);
202 }
203
204 private void notifyTcObservers(V source, V target, boolean isInsertion) {
205 if (isInsertion) {
206 for (ITcObserver<V> o : observers) {
207 o.tupleInserted(source, target);
208 }
209 } else {
210 for (ITcObserver<V> o : observers) {
211 o.tupleDeleted(source, target);
212 }
213 }
214 }
215
216 @Override
217 public void dispose() {
218 tc.clear();
219 this.gds.detachObserver(this);
220 }
221
222 @Override
223 public IGraphPathFinder<V> getPathFinder() {
224 return new DFSPathFinder<V>(gds, this);
225 }
226}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.counting;
11
12import java.util.Collections;
13import java.util.List;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.rete.itc.alg.misc.topsort.TopologicalSorting;
17import tools.refinery.viatra.runtime.rete.itc.alg.misc.ITcRelation;
18import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
21import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
22import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
23import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity;
24
25/**
26 * Transitive closure relation implementation for the Counting algorithm.
27 *
28 * @author Tamas Szabo
29 *
30 * @param <V>
31 */
32public class CountingTcRelation<V> implements ITcRelation<V> {
33
34 private IMultiLookup<V, V> tuplesForward = null;
35 private IMultiLookup<V, V> tuplesBackward = null;
36
37 protected CountingTcRelation(boolean backwardIndexing) {
38 tuplesForward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class);
39 if (backwardIndexing)
40 tuplesBackward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class);
41 }
42
43 protected boolean isEmpty() {
44 return 0 == this.tuplesForward.countKeys();
45 }
46
47 protected void clear() {
48 this.tuplesForward.clear();
49
50 if (tuplesBackward != null) {
51 this.tuplesBackward.clear();
52 }
53 }
54
55 protected void union(CountingTcRelation<V> rA) {
56 IMultiLookup<V, V> rForward = rA.tuplesForward;
57 for (V source : rForward.distinctKeys()) {
58 IMemoryView<V> targetBag = rForward.lookup(source);
59 for (V target : targetBag.distinctValues()) {
60 this.addTuple(source, target, targetBag.getCount(target));
61 }
62 }
63 }
64
65 public int getCount(V source, V target) {
66 IMemoryView<V> bucket = tuplesForward.lookup(source);
67 return bucket == null ? 0 : bucket.getCount(target);
68 }
69
70 /**
71 * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false
72 * otherwise (in this case count is incremented with the given count parameter).
73 *
74 * @param source
75 * the source of the tuple
76 * @param target
77 * the target of the tuple
78 * @param count
79 * the count of the tuple, must be positive
80 * @return true if the relation did not contain previously the tuple
81 */
82 public boolean addTuple(V source, V target, int count) {
83 if (tuplesBackward != null) {
84 tuplesBackward.addPairPositiveMultiplicity(target, source, count);
85 }
86
87 ChangeGranularity change =
88 tuplesForward.addPairPositiveMultiplicity(source, target, count);
89
90 return change != ChangeGranularity.DUPLICATE;
91 }
92
93 /**
94 * Derivation count of the tuple (source,target) is incremented or decremented.
95 * Returns true iff updated to / from zero derivation count.
96 * @since 1.7
97 */
98 public boolean updateTuple(V source, V target, boolean isInsertion) {
99 if (isInsertion) {
100 if (tuplesBackward != null) {
101 tuplesBackward.addPair(target, source);
102 }
103 ChangeGranularity change =
104 tuplesForward.addPair(source, target);
105 return change != ChangeGranularity.DUPLICATE;
106 } else {
107 if (tuplesBackward != null) {
108 tuplesBackward.removePair(target, source);
109 }
110 ChangeGranularity change =
111 tuplesForward.removePair(source, target);
112 return change != ChangeGranularity.DUPLICATE;
113 }
114 }
115
116 public void deleteTupleEnd(V deleted) {
117 Set<V> sourcesToDelete = CollectionsFactory.createSet();
118 Set<V> targetsToDelete = CollectionsFactory.createSet();
119
120 for (V target : tuplesForward.lookupOrEmpty(deleted).distinctValues()) {
121 targetsToDelete.add(target);
122 }
123 if (tuplesBackward != null) {
124 for (V source : tuplesBackward.lookupOrEmpty(deleted).distinctValues()) {
125 sourcesToDelete.add(source);
126 }
127 } else {
128 for (V sourceCandidate : tuplesForward.distinctKeys()) {
129 if (tuplesForward.lookupOrEmpty(sourceCandidate).containsNonZero(deleted))
130 sourcesToDelete.add(sourceCandidate);
131 }
132 }
133
134 for (V source : sourcesToDelete) {
135 int count = tuplesForward.lookupOrEmpty(source).getCount(deleted);
136 for (int i=0; i< count; ++i) tuplesForward.removePair(source, deleted);
137 }
138 for (V target : targetsToDelete) {
139 int count = tuplesForward.lookupOrEmpty(deleted).getCount(target);
140 for (int i=0; i< count; ++i) tuplesForward.removePair(deleted, target);
141 }
142
143 if (tuplesBackward != null) {
144 for (V source : sourcesToDelete) {
145 int count = tuplesBackward.lookupOrEmpty(deleted).getCount(source);
146 for (int i=0; i< count; ++i) tuplesBackward.removePair(deleted, source);
147 }
148 for (V target : targetsToDelete) {
149 int count = tuplesBackward.lookupOrEmpty(target).getCount(deleted);
150 for (int i=0; i< count; ++i) tuplesBackward.removePair(target, deleted);
151 }
152 }
153 }
154
155 @Override
156 public String toString() {
157 StringBuilder sb = new StringBuilder("TcRelation = ");
158
159 for (V source : tuplesForward.distinctKeys()) {
160 IMemoryView<V> targets = tuplesForward.lookup(source);
161 for (V target : targets.distinctValues()) {
162 sb.append("{(" + source + "," + target + ")," + targets.getCount(target) + "} ");
163 }
164 }
165
166 return sb.toString();
167 }
168
169 @Override
170 public Set<V> getTupleEnds(V source) {
171 IMemoryView<V> tupEnds = tuplesForward.lookup(source);
172 if (tupEnds == null)
173 return null;
174 return tupEnds.distinctValues();
175 }
176
177 /**
178 * Returns the set of nodes from which the target node is reachable, if already computed.
179 *
180 * @param target
181 * the target node
182 * @return the set of source nodes
183 * @throws UnsupportedOperationException if backwards index not computed
184 */
185 public Set<V> getTupleStarts(V target) {
186 if (tuplesBackward != null) {
187 IMemoryView<V> tupStarts = tuplesBackward.lookup(target);
188 if (tupStarts == null)
189 return null;
190 return tupStarts.distinctValues();
191 } else {
192 throw new UnsupportedOperationException("built without backward indexing");
193 }
194 }
195
196 @Override
197 public Set<V> getTupleStarts() {
198 Set<V> nodes = CollectionsFactory.createSet();
199 for (V s : tuplesForward.distinctKeys()) {
200 nodes.add(s);
201 }
202 return nodes;
203 }
204
205 /**
206 * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise.
207 *
208 * @param source
209 * the source node
210 * @param target
211 * the target node
212 * @return true if tuple is present, false otherwise
213 */
214 public boolean containsTuple(V source, V target) {
215 return tuplesForward.lookupOrEmpty(source).containsNonZero(target);
216 }
217
218 @SuppressWarnings("unchecked")
219 @Override
220 public boolean equals(Object obj) {
221 if (this == obj) {
222 return true;
223 } else if (obj == null || this.getClass() != obj.getClass()) {
224 return false;
225 } else {
226 CountingTcRelation<V> aTR = (CountingTcRelation<V>) obj;
227
228 return tuplesForward.equals(aTR.tuplesForward);
229 }
230 }
231
232 @Override
233 public int hashCode() {
234 return tuplesForward.hashCode();
235 }
236
237 public static <V> CountingTcRelation<V> createFrom(IBiDirectionalGraphDataSource<V> gds) {
238 List<V> topologicalSorting = TopologicalSorting.compute(gds);
239 CountingTcRelation<V> tc = new CountingTcRelation<V>(true);
240 Collections.reverse(topologicalSorting);
241 for (V n : topologicalSorting) {
242 IMemoryView<V> sourceNodes = gds.getSourceNodes(n);
243 Set<V> tupEnds = tc.getTupleEnds(n);
244 for (V s : sourceNodes.distinctValues()) {
245 int count = sourceNodes.getCount(s);
246 for (int i = 0; i < count; i++) {
247 tc.updateTuple(s, n, true);
248 if (tupEnds != null) {
249 for (V t : tupEnds) {
250 tc.updateTuple(s, t, true);
251 }
252 }
253 }
254 }
255 }
256
257 return tc;
258 }
259}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.itc.alg.incscc;
10
11import tools.refinery.viatra.runtime.rete.itc.igraph.ITcObserver;
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13
14/**
15 * @author Tamas Szabo
16 *
17 */
18public class CountingListener<V> implements ITcObserver<V> {
19
20 private IncSCCAlg<V> alg;
21
22 public CountingListener(IncSCCAlg<V> alg) {
23 this.alg = alg;
24 }
25
26 @Override
27 public void tupleInserted(V source, V target) {
28 alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.INSERT);
29 }
30
31 @Override
32 public void tupleDeleted(V source, V target) {
33 alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.DELETE);
34 }
35
36}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.incscc;
11
12import tools.refinery.viatra.runtime.matchers.algorithms.UnionFind;
13import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
14import tools.refinery.viatra.runtime.matchers.util.Direction;
15import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
16import tools.refinery.viatra.runtime.rete.itc.alg.counting.CountingAlg;
17import tools.refinery.viatra.runtime.rete.itc.alg.misc.DFSPathFinder;
18import tools.refinery.viatra.runtime.rete.itc.alg.misc.GraphHelper;
19import tools.refinery.viatra.runtime.rete.itc.alg.misc.IGraphPathFinder;
20import tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple;
21import tools.refinery.viatra.runtime.rete.itc.alg.misc.bfs.BFS;
22import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCC;
23import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCCResult;
24import tools.refinery.viatra.runtime.rete.itc.alg.util.CollectionHelper;
25import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph;
26import tools.refinery.viatra.runtime.rete.itc.igraph.*;
27
28import java.util.*;
29import java.util.Map.Entry;
30
31/**
32 * Incremental SCC maintenance + counting algorithm.
33 *
34 * @author Tamas Szabo
35 *
36 * @param <V>
37 * the type parameter of the nodes in the graph data source
38 */
39public class IncSCCAlg<V> implements IGraphObserver<V>, ITcDataSource<V> {
40
41 public UnionFind<V> sccs;
42 public IBiDirectionalGraphDataSource<V> gds;
43 private CountingAlg<V> counting;
44 private Graph<V> reducedGraph;
45 private IBiDirectionalGraphDataSource<V> reducedGraphIndexer;
46 private List<ITcObserver<V>> observers;
47 private CountingListener<V> countingListener;
48
49 public IncSCCAlg(IGraphDataSource<V> graphDataSource) {
50
51 if (graphDataSource instanceof IBiDirectionalGraphDataSource<?>) {
52 gds = (IBiDirectionalGraphDataSource<V>) graphDataSource;
53 } else {
54 gds = new IBiDirectionalWrapper<V>(graphDataSource);
55 }
56 observers = CollectionsFactory.createObserverList();
57 sccs = new UnionFind<V>();
58 reducedGraph = new Graph<V>();
59 reducedGraphIndexer = new IBiDirectionalWrapper<V>(reducedGraph);
60 countingListener = new CountingListener<V>(this);
61 initalizeInternalDataStructures();
62 gds.attachObserver(this);
63 }
64
65 private void initalizeInternalDataStructures() {
66 SCCResult<V> _sccres = SCC.computeSCC(gds);
67 Set<Set<V>> _sccs = _sccres.getSccs();
68
69 for (Set<V> _set : _sccs) {
70 sccs.makeSet(_set);
71 }
72
73 // Initalization of the reduced graph
74 for (V n : sccs.getPartitionHeads()) {
75 reducedGraph.insertNode(n);
76 }
77
78 for (V source : gds.getAllNodes()) {
79 final IMemoryView<V> targetNodes = gds.getTargetNodes(source);
80 for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) {
81 for (int i = 0; i < entry.getValue(); i++) {
82 V target = entry.getKey();
83 V sourceRoot = sccs.find(source);
84 V targetRoot = sccs.find(target);
85
86 if (!sourceRoot.equals(targetRoot)) {
87 reducedGraph.insertEdge(sourceRoot, targetRoot);
88 }
89 }
90 }
91 }
92
93 counting = new CountingAlg<V>(reducedGraph);
94 }
95
96 @Override
97 public void edgeInserted(V source, V target) {
98 V sourceRoot = sccs.find(source);
99 V targetRoot = sccs.find(target);
100
101 // Different SCC
102 if (!sourceRoot.equals(targetRoot)) {
103
104 // source is reachable from target?
105 if (counting.isReachable(targetRoot, sourceRoot)) {
106
107 Set<V> predecessorRoots = counting.getAllReachableSources(sourceRoot);
108 Set<V> successorRoots = counting.getAllReachableTargets(targetRoot);
109
110 // 1. intersection of source and target roots, these will be in the merged SCC
111 Set<V> isectRoots = CollectionHelper.intersection(predecessorRoots, successorRoots);
112 isectRoots.add(sourceRoot);
113 isectRoots.add(targetRoot);
114
115 // notifications must be issued before Union-Find modifications
116 if (observers.size() > 0) {
117 Set<V> sourceSCCs = createSetNullTolerant(predecessorRoots);
118 sourceSCCs.add(sourceRoot);
119 Set<V> targetSCCs = createSetNullTolerant(successorRoots);
120 targetSCCs.add(targetRoot);
121
122 // tracing back to actual nodes
123 for (V sourceSCC : sourceSCCs) {
124 targetLoop: for (V targetSCC : targetSCCs) {
125 if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop;
126
127 boolean needsNotification =
128 // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc.
129 // Issue notifications only if there is no self-loop present at the moment
130 (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper
131 .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0)
132 ||
133 // Case 2. sourceSCC and targetSCC are different sccs.
134 (!sourceSCC.equals(targetSCC));
135 // if self loop is already present omit the notification
136 if (needsNotification) {
137 notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC),
138 Direction.INSERT);
139 }
140 }
141 }
142 }
143
144 // 2. delete edges, nodes
145 List<V> sourceSCCs = new ArrayList<V>();
146 List<V> targetSCCs = new ArrayList<V>();
147
148 for (V r : isectRoots) {
149 List<V> sourceSCCsOfSCC = getSourceSCCsOfSCC(r);
150 List<V> targetSCCsOfSCC = getTargetSCCsOfSCC(r);
151
152 for (V sourceSCC : sourceSCCsOfSCC) {
153 if (!sourceSCC.equals(r)) {
154 reducedGraph.deleteEdgeIfExists(sourceSCC, r);
155 }
156 }
157
158 for (V targetSCC : targetSCCsOfSCC) {
159 if (!isectRoots.contains(targetSCC) && !r.equals(targetSCC)) {
160 reducedGraph.deleteEdgeIfExists(r, targetSCC);
161 }
162 }
163
164 sourceSCCs.addAll(sourceSCCsOfSCC);
165 targetSCCs.addAll(targetSCCsOfSCC);
166 }
167
168 for (V r : isectRoots) {
169 reducedGraph.deleteNode(r);
170 }
171
172 // 3. union
173 Iterator<V> iterator = isectRoots.iterator();
174 V newRoot = iterator.next();
175 while (iterator.hasNext()) {
176 newRoot = sccs.union(newRoot, iterator.next());
177 }
178
179 // 4. add new node
180 reducedGraph.insertNode(newRoot);
181
182 // 5. add edges
183 Set<V> containedNodes = sccs.getPartition(newRoot);
184
185 for (V sourceSCC : sourceSCCs) {
186 if (!containedNodes.contains(sourceSCC) && !sourceSCC.equals(newRoot)) {
187 reducedGraph.insertEdge(sourceSCC, newRoot);
188 }
189 }
190 for (V targetSCC : targetSCCs) {
191 if (!containedNodes.contains(targetSCC) && !targetSCC.equals(newRoot)) {
192 reducedGraph.insertEdge(newRoot, targetSCC);
193 }
194 }
195 } else {
196 if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 1) {
197 counting.attachObserver(countingListener);
198 }
199 reducedGraph.insertEdge(sourceRoot, targetRoot);
200 counting.detachObserver(countingListener);
201 }
202 } else {
203 // Notifications about self-loops
204 if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1
205 && GraphHelper.getEdgeCount(source, target, gds) == 1) {
206 notifyTcObservers(source, source, Direction.INSERT);
207 }
208 }
209 }
210
211 @Override
212 public void edgeDeleted(V source, V target) {
213 V sourceRoot = sccs.find(source);
214 V targetRoot = sccs.find(target);
215
216 if (!sourceRoot.equals(targetRoot)) {
217 if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 0) {
218 counting.attachObserver(countingListener);
219 }
220 reducedGraph.deleteEdgeIfExists(sourceRoot, targetRoot);
221 counting.detachObserver(countingListener);
222 } else {
223 // get the graph for the scc whose root is sourceRoot
224 Graph<V> g = GraphHelper.getSubGraph(sccs.getPartition(sourceRoot), gds);
225
226 // if source is not reachable from target anymore
227 if (!BFS.isReachable(source, target, g)) {
228 // create copies of the current state before destructive manipulation
229 Map<V, Integer> reachableSources = CollectionsFactory.createMap();
230 for (Entry<V, Integer> entry : reducedGraphIndexer.getSourceNodes(sourceRoot).entriesWithMultiplicities()) {
231 reachableSources.put(entry.getKey(), entry.getValue());
232 }
233 Map<V, Integer> reachableTargets = CollectionsFactory.createMap();
234 for (Entry<V, Integer> entry : reducedGraphIndexer.getTargetNodes(sourceRoot).entriesWithMultiplicities()) {
235 reachableTargets.put(entry.getKey(), entry.getValue());
236 }
237
238 SCCResult<V> _newSccs = SCC.computeSCC(g);
239
240 // delete scc node (and with its edges too)
241 for (Entry<V, Integer> entry : reachableSources.entrySet()) {
242 V s = entry.getKey();
243 for (int i = 0; i < entry.getValue(); i++) {
244 reducedGraph.deleteEdgeIfExists(s, sourceRoot);
245 }
246 }
247
248 for (Entry<V, Integer> entry : reachableTargets.entrySet()) {
249 V t = entry.getKey();
250 for (int i = 0; i < entry.getValue(); i++) {
251 reducedGraph.deleteEdgeIfExists(sourceRoot, t);
252 }
253 }
254
255 sccs.deleteSet(sourceRoot);
256 reducedGraph.deleteNode(sourceRoot);
257
258 Set<Set<V>> newSCCs = _newSccs.getSccs();
259 Set<V> newSCCRoots = CollectionsFactory.createSet();
260
261 // add new nodes and edges to the reduced graph
262 for (Set<V> newSCC : newSCCs) {
263 V newRoot = sccs.makeSet(newSCC);
264 reducedGraph.insertNode(newRoot);
265 newSCCRoots.add(newRoot);
266 }
267 for (V newSCCRoot : newSCCRoots) {
268 List<V> sourceSCCsOfSCC = getSourceSCCsOfSCC(newSCCRoot);
269 List<V> targetSCCsOfSCC = getTargetSCCsOfSCC(newSCCRoot);
270
271 for (V sourceSCC : sourceSCCsOfSCC) {
272 if (!sourceSCC.equals(newSCCRoot)) {
273 reducedGraph.insertEdge(sccs.find(sourceSCC), newSCCRoot);
274 }
275 }
276 for (V targetSCC : targetSCCsOfSCC) {
277 if (!newSCCRoots.contains(targetSCC) && !targetSCC.equals(newSCCRoot))
278 reducedGraph.insertEdge(newSCCRoot, targetSCC);
279 }
280 }
281
282 // Must be after the union-find modifications
283 if (observers.size() > 0) {
284 V newSourceRoot = sccs.find(source);
285 V newTargetRoot = sccs.find(target);
286
287 Set<V> sourceSCCs = createSetNullTolerant(counting.getAllReachableSources(newSourceRoot));
288 sourceSCCs.add(newSourceRoot);
289
290 Set<V> targetSCCs = createSetNullTolerant(counting.getAllReachableTargets(newTargetRoot));
291 targetSCCs.add(newTargetRoot);
292
293 for (V sourceSCC : sourceSCCs) {
294 targetLoop: for (V targetSCC : targetSCCs) {
295 if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop;
296
297 boolean needsNotification =
298 // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc.
299 // Issue notifications only if there is no self-loop present at the moment
300 (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper
301 .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0)
302 ||
303 // Case 2. sourceSCC and targetSCC are different sccs.
304 (!sourceSCC.equals(targetSCC));
305 // if self loop is already present omit the notification
306 if (needsNotification) {
307 notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC),
308 Direction.DELETE);
309 }
310 }
311 }
312 }
313 } else {
314 // only handle self-loop notifications - sourceRoot equals to targetRoot
315 if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1
316 && GraphHelper.getEdgeCount(source, target, gds) == 0) {
317 notifyTcObservers(source, source, Direction.DELETE);
318 }
319 }
320 }
321 }
322
323 @Override
324 public void nodeInserted(V n) {
325 sccs.makeSet(n);
326 reducedGraph.insertNode(n);
327 }
328
329 @Override
330 public void nodeDeleted(V n) {
331 IMemoryView<V> sources = gds.getSourceNodes(n);
332 IMemoryView<V> targets = gds.getTargetNodes(n);
333
334 for (Entry<V, Integer> entry : sources.entriesWithMultiplicities()) {
335 for (int i = 0; i < entry.getValue(); i++) {
336 V source = entry.getKey();
337 edgeDeleted(source, n);
338 }
339 }
340
341 for (Entry<V, Integer> entry : targets.entriesWithMultiplicities()) {
342 for (int i = 0; i < entry.getValue(); i++) {
343 V target = entry.getKey();
344 edgeDeleted(n, target);
345 }
346 }
347
348 sccs.deleteSet(n);
349 }
350
351 @Override
352 public void attachObserver(ITcObserver<V> to) {
353 observers.add(to);
354 }
355
356 @Override
357 public void detachObserver(ITcObserver<V> to) {
358 observers.remove(to);
359 }
360
361 @Override
362 public Set<V> getAllReachableTargets(V source) {
363 V sourceRoot = sccs.find(source);
364 Set<V> containedNodes = sccs.getPartition(sourceRoot);
365 Set<V> targets = CollectionsFactory.createSet();
366
367 if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(source, gds) == 1) {
368 targets.addAll(containedNodes);
369 }
370
371 Set<V> rootSet = counting.getAllReachableTargets(sourceRoot);
372 if (rootSet != null) {
373 for (V _root : rootSet) {
374 targets.addAll(sccs.getPartition(_root));
375 }
376 }
377
378 return targets;
379 }
380
381 @Override
382 public Set<V> getAllReachableSources(V target) {
383 V targetRoot = sccs.find(target);
384 Set<V> containedNodes = sccs.getPartition(targetRoot);
385 Set<V> sources = CollectionsFactory.createSet();
386
387 if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(target, gds) == 1) {
388 sources.addAll(containedNodes);
389 }
390
391 Set<V> rootSet = counting.getAllReachableSources(targetRoot);
392 if (rootSet != null) {
393 for (V _root : rootSet) {
394 sources.addAll(sccs.getPartition(_root));
395 }
396 }
397 return sources;
398 }
399
400 @Override
401 public boolean isReachable(V source, V target) {
402 V sourceRoot = sccs.find(source);
403 V targetRoot = sccs.find(target);
404
405 if (sourceRoot.equals(targetRoot))
406 return true;
407 else
408 return counting.isReachable(sourceRoot, targetRoot);
409 }
410
411 public List<V> getReachabilityPath(V source, V target) {
412 if (!isReachable(source, target)) {
413 return null;
414 } else {
415 Set<V> sccsInSubGraph = CollectionHelper.intersection(counting.getAllReachableTargets(source),
416 counting.getAllReachableSources(target));
417 sccsInSubGraph.add(sccs.find(source));
418 sccsInSubGraph.add(sccs.find(target));
419 Set<V> nodesInSubGraph = CollectionsFactory.createSet();
420
421 for (V sccRoot : sccsInSubGraph) {
422 nodesInSubGraph.addAll(sccs.getPartition(sccRoot));
423 }
424
425 return GraphHelper.constructPath(source, target, nodesInSubGraph, gds);
426 }
427 }
428
429 /**
430 * Return the SCCs from which the SCC represented by the root node is reachable. Note that an SCC can be present
431 * multiple times in the returned list (multiple edges between the two SCCs).
432 *
433 * @param root
434 * @return the list of reachable target SCCs
435 */
436 private List<V> getSourceSCCsOfSCC(V root) {
437 List<V> sourceSCCs = new ArrayList<V>();
438
439 for (V containedNode : this.sccs.getPartition(root)) {
440 IMemoryView<V> sourceNodes = this.gds.getSourceNodes(containedNode);
441 for (V source : sourceNodes.distinctValues()) {
442 sourceSCCs.add(this.sccs.find(source));
443 }
444 }
445
446 return sourceSCCs;
447 }
448
449 /**
450 * Returns true if the SCC represented by the given root node has incoming edges in the reduced graph,
451 * false otherwise (if this SCC is a source in the reduced graph).
452 *
453 * @param root the root node of an SCC
454 * @return true if it has incoming edges, false otherwise
455 * @since 1.6
456 */
457 public boolean hasIncomingEdges(final V root) {
458 for (final V containedNode : this.sccs.getPartition(root)) {
459 final IMemoryView<V> sourceNodes = this.gds.getSourceNodes(containedNode);
460 for (final V source : sourceNodes.distinctValues()) {
461 final V otherRoot = this.sccs.find(source);
462 if (!Objects.equals(root, otherRoot)) {
463 return true;
464 }
465 }
466 }
467 return false;
468 }
469
470 /**
471 * Return the SCCs which are reachable from the SCC represented by the root node. Note that an SCC can be present
472 * multiple times in the returned list (multiple edges between the two SCCs).
473 *
474 * @param root
475 * @return the list of reachable target SCCs
476 */
477 private List<V> getTargetSCCsOfSCC(V root) {
478 List<V> targetSCCs = new ArrayList<V>();
479
480 for (V containedNode : this.sccs.getPartition(root)) {
481 IMemoryView<V> targetNodes = this.gds.getTargetNodes(containedNode);
482 for (V target : targetNodes.distinctValues()) {
483 targetSCCs.add(this.sccs.find(target));
484 }
485 }
486
487 return targetSCCs;
488 }
489
490 /**
491 * Returns true if the SCC represented by the given root node has outgoing edges in the reduced graph,
492 * false otherwise (if this SCC is a sink in the reduced graph).
493 *
494 * @param root the root node of an SCC
495 * @return true if it has outgoing edges, false otherwise
496 * @since 1.6
497 */
498 public boolean hasOutgoingEdges(V root) {
499 for (final V containedNode : this.sccs.getPartition(root)) {
500 final IMemoryView<V> targetNodes = this.gds.getTargetNodes(containedNode);
501 for (final V target : targetNodes.distinctValues()) {
502 final V otherRoot = this.sccs.find(target);
503 if (!Objects.equals(root, otherRoot)) {
504 return true;
505 }
506 }
507 }
508 return false;
509 }
510
511 @Override
512 public void dispose() {
513 gds.detachObserver(this);
514 counting.dispose();
515 }
516
517 /**
518 * Call this method to notify the observers of the transitive closure relation. The tuples used in the notification
519 * will be the Descartes product of the two sets given.
520 *
521 * @param sources
522 * the source nodes
523 * @param targets
524 * the target nodes
525 * @param direction
526 */
527 protected void notifyTcObservers(Set<V> sources, Set<V> targets, Direction direction) {
528 for (V s : sources) {
529 for (V t : targets) {
530 notifyTcObservers(s, t, direction);
531 }
532 }
533 }
534
535 private void notifyTcObservers(V source, V target, Direction direction) {
536 for (ITcObserver<V> observer : observers) {
537 if (direction == Direction.INSERT) {
538 observer.tupleInserted(source, target);
539 }
540 if (direction == Direction.DELETE) {
541 observer.tupleDeleted(source, target);
542 }
543 }
544 }
545
546 /**
547 * Returns the node that is selected as the representative of the SCC containing the argument.
548 * @since 1.6
549 */
550 public V getRepresentative(V node) {
551 return sccs.find(node);
552 }
553
554 public Set<Tuple<V>> getTcRelation() {
555 Set<Tuple<V>> resultSet = new HashSet<Tuple<V>>();
556
557 for (V sourceRoot : sccs.getPartitionHeads()) {
558 Set<V> sources = sccs.getPartition(sourceRoot);
559 if (sources.size() > 1 || GraphHelper.getEdgeCount(sources.iterator().next(), gds) == 1) {
560 for (V source : sources) {
561 for (V target : sources) {
562 resultSet.add(new Tuple<V>(source, target));
563 }
564 }
565 }
566
567 Set<V> reachableTargets = counting.getAllReachableTargets(sourceRoot);
568 if (reachableTargets != null) {
569 for (V targetRoot : reachableTargets) {
570 for (V source : sources) {
571 for (V target : sccs.getPartition(targetRoot)) {
572 resultSet.add(new Tuple<V>(source, target));
573 }
574 }
575 }
576 }
577 }
578
579 return resultSet;
580 }
581
582 public boolean isIsolated(V node) {
583 IMemoryView<V> targets = gds.getTargetNodes(node);
584 IMemoryView<V> sources = gds.getSourceNodes(node);
585 return targets.isEmpty() && sources.isEmpty();
586 }
587
588 @Override
589 public IGraphPathFinder<V> getPathFinder() {
590 return new DFSPathFinder<V>(gds, this);
591 }
592
593 /**
594 * The graph of SCCs; each SCC is represented by its representative node (see {@link #getRepresentative(Object)})
595 * @since 1.6
596 */
597 public Graph<V> getReducedGraph() {
598 return reducedGraph;
599 }
600
601 private static <V> Set<V> createSetNullTolerant(Set<V> initial) {
602 if (initial != null)
603 return CollectionsFactory.createSet(initial);
604 else
605 return CollectionsFactory.createSet();
606 }
607
608
609}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Abel Hegedus and IncQueryLabs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.itc.alg.misc;
10
11import java.util.ArrayList;
12import java.util.Deque;
13import java.util.HashSet;
14import java.util.Iterator;
15import java.util.LinkedList;
16import java.util.List;
17import java.util.Set;
18
19import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource;
20import tools.refinery.viatra.runtime.rete.itc.igraph.ITcDataSource;
21import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
22
23/**
24 * A depth-first search implementation of the {@link IGraphPathFinder}.
25 *
26 * TODO use ITC to filter nodes that must be traversed, instead of checks
27 *
28 * @author Abel Hegedus
29 *
30 * @param <V>
31 * the node type of the graph
32 */
33public class DFSPathFinder<V> implements IGraphPathFinder<V> {
34
35 private IGraphDataSource<V> graph;
36 private ITcDataSource<V> itc;
37
38 public DFSPathFinder(IGraphDataSource<V> graph, ITcDataSource<V> itc) {
39 this.graph = graph;
40 this.itc = itc;
41 }
42
43 @Override
44 public Iterable<Deque<V>> getAllPaths(V sourceNode, V targetNode) {
45 Set<V> endNodes = new HashSet<V>();
46 endNodes.add(targetNode);
47 return getAllPathsToTargets(sourceNode, endNodes);
48 }
49
50 @Override
51 public Iterable<Deque<V>> getAllPathsToTargets(V sourceNode, Set<V> targetNodes) {
52 List<Deque<V>> paths = new ArrayList<Deque<V>>();
53 Deque<V> visited = new LinkedList<V>();
54 Set<V> reachableTargets = new HashSet<V>();
55 for (V targetNode : targetNodes) {
56 if (itc.isReachable(sourceNode, targetNode)) {
57 reachableTargets.add(targetNode);
58 }
59 }
60 if (!reachableTargets.isEmpty()) {
61 return paths;
62 }
63 visited.add(sourceNode);
64 return getPaths(paths, visited, reachableTargets);
65 }
66
67 protected Iterable<Deque<V>> getPaths(List<Deque<V>> paths, Deque<V> visited, Set<V> targetNodes) {
68 IMemoryView<V> nodes = graph.getTargetNodes(visited.getLast());
69 // examine adjacent nodes
70 for (V node : nodes.distinctValues()) {
71 if (visited.contains(node)) {
72 continue;
73 }
74 if (targetNodes.contains(node)) {
75 visited.add(node);
76 // clone visited LinkedList
77 Deque<V> visitedClone = new LinkedList<V>(visited);
78 paths.add(visitedClone);
79 visited.removeLast();
80 break;
81 }
82 }
83
84 // in breadth-first, recursion needs to come after visiting connected nodes
85 for (V node : nodes.distinctValues()) {
86 if (visited.contains(node) || targetNodes.contains(node)) {
87 continue;
88 }
89 boolean canReachTarget = false;
90 for (V target : targetNodes) {
91 if (itc.isReachable(node, target)) {
92 canReachTarget = true;
93 break;
94 }
95 }
96 if (canReachTarget) {
97 visited.addLast(node);
98 getPaths(paths, visited, targetNodes);
99 visited.removeLast();
100 }
101 }
102
103 return paths;
104 }
105
106 public String printPaths(List<Deque<V>> paths) {
107 StringBuilder sb = new StringBuilder();
108 for (Deque<V> visited : paths) {
109 sb.append("Path: ");
110 for (V node : visited) {
111 sb.append(node);
112 sb.append(" --> ");
113 }
114 sb.append("\n");
115 }
116 return sb.toString();
117 }
118
119 @Override
120 public Deque<V> getPath(V sourceNode, V targetNode) {
121 // TODO optimize
122 Iterable<Deque<V>> allPaths = getAllPaths(sourceNode, targetNode);
123 Iterator<Deque<V>> pathIterator = allPaths.iterator();
124 return pathIterator.hasNext() ? pathIterator.next() : new LinkedList<V>();
125 }
126
127 @Override
128 public Iterable<Deque<V>> getShortestPaths(V sourceNode, V targetNode) {
129 // TODO optimize
130 Iterable<Deque<V>> allPaths = getAllPaths(sourceNode, targetNode);
131 List<Deque<V>> shortestPaths = new ArrayList<Deque<V>>();
132 int shortestPathLength = -1;
133 for (Deque<V> path : allPaths) {
134 int pathLength = path.size();
135 if (shortestPathLength == -1 || pathLength < shortestPathLength) {
136 shortestPaths.clear();
137 shortestPathLength = pathLength;
138 }
139 if (pathLength == shortestPathLength) {
140 shortestPaths.add(path);
141 }
142 }
143 return shortestPaths;
144 }
145
146}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.misc;
11
12public class Edge<V> {
13 private V source;
14 private V target;
15
16 public Edge(V source, V target) {
17 super();
18 this.source = source;
19 this.target = target;
20 }
21
22 public V getSource() {
23 return source;
24 }
25
26 public void setSource(V source) {
27 this.source = source;
28 }
29
30 public V getTarget() {
31 return target;
32 }
33
34 public void setTarget(V target) {
35 this.target = target;
36 }
37
38}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.itc.alg.misc;
10
11import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
12import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph;
13import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource;
14import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource;
15
16import java.util.*;
17import java.util.Map.Entry;
18
19/**
20 * Utility class for graph related operations.
21 *
22 * @author Tamas Szabo
23 */
24public class GraphHelper {
25
26 private GraphHelper() {/*Utility class constructor*/}
27
28 /**
29 * Returns the subgraph from the given {@link IBiDirectionalGraphDataSource} which contains the given set of nodes.
30 *
31 * @param nodesInSubGraph
32 * the nodes that are present in the subgraph
33 * @param graphDataSource
34 * the graph data source for the original graph
35 * @return the subgraph associated to the given nodes
36 */
37 public static <V> Graph<V> getSubGraph(Collection<V> nodesInSubGraph,
38 IBiDirectionalGraphDataSource<V> graphDataSource) {
39 Graph<V> g = new Graph<V>();
40 if (nodesInSubGraph != null) {
41 for (V node : nodesInSubGraph) {
42 g.insertNode(node);
43 }
44
45 for (V node : nodesInSubGraph) {
46 IMemoryView<V> sources = graphDataSource.getSourceNodes(node);
47 for (Entry<V, Integer> entry : sources.entriesWithMultiplicities()) {
48 for (int i = 0; i < entry.getValue(); i++) {
49 V s = entry.getKey();
50 if (nodesInSubGraph.contains(s)) {
51 g.insertEdge(s, node);
52 }
53 }
54 }
55 }
56 }
57
58 return g;
59 }
60
61 /**
62 * Constructs a path between source and target in the given graph. Both the {@link IGraphDataSource} and the set of
63 * nodes are used, this way it is possible to construct a path in a given subgraph.
64 *
65 * The returned {@link List} contains the nodes along the path (this means that there is an edge in the graph
66 * between two consecutive nodes). A self loop (one edge) is indicated with the source node being present two times
67 * in the returned {@link List}.
68 *
69 * @param source
70 * the source node
71 * @param target
72 * the target node
73 * @param nodesInGraph
74 * the nodes that are present in the subgraph
75 * @param graphDataSource
76 * the graph data source
77 * @return the path between the two nodes
78 */
79 public static <V> List<V> constructPath(V source, V target, Set<V> nodesInGraph,
80 IGraphDataSource<V> graphDataSource) {
81 Set<V> visitedNodes = new HashSet<V>();
82 List<V> path = new ArrayList<V>();
83
84 visitedNodes.add(source);
85 path.add(source);
86 V act = source;
87
88 // if source and target are the same node
89 if (source.equals(target) && graphDataSource.getTargetNodes(source).containsNonZero(target)) {
90 // the node will be present in the path two times
91 path.add(source);
92 return path;
93 } else {
94 while (act != null) {
95 V nextNode = getNextNodeToVisit(act, graphDataSource, nodesInGraph, visitedNodes);
96 if (nextNode == null && path.size() > 1) {
97 // needs to backtrack along path
98 // remove the last element in the path because we can't go
99 // anywhere from there
100 path.remove(path.size() - 1);
101 while (nextNode == null && path.size() > 0) {
102 V lastPathElement = path.get(path.size() - 1);
103 nextNode = getNextNodeToVisit(lastPathElement, graphDataSource, nodesInGraph, visitedNodes);
104 if (nextNode == null) {
105 path.remove(path.size() - 1);
106 }
107 }
108 }
109
110 if (nextNode != null) {
111 visitedNodes.add(nextNode);
112 path.add(nextNode);
113 if (nextNode.equals(target)) {
114 return path;
115 }
116 }
117 act = nextNode;
118 }
119 return null;
120 }
121 }
122
123 private static <V> V getNextNodeToVisit(V act, IGraphDataSource<V> graphDataSource, Set<V> nodesInSubGraph,
124 Set<V> visitedNodes) {
125 IMemoryView<V> targetNodes = graphDataSource.getTargetNodes(act);
126 for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) {
127 for (int i = 0; i < entry.getValue(); i++) {
128 V node = entry.getKey();
129 if (nodesInSubGraph.contains(node) && !visitedNodes.contains(node)) {
130 return node;
131 }
132 }
133 }
134 return null;
135 }
136
137 /**
138 * Returns the number of self-loop edges for the given node.
139 *
140 * @param node
141 * the node
142 * @param graphDataSource
143 * the graph data source
144 * @return the number of self-loop edges
145 */
146 public static <V> int getEdgeCount(V node, IGraphDataSource<V> graphDataSource) {
147 return getEdgeCount(node, node, graphDataSource);
148 }
149
150 /**
151 * Returns the number of edges between the given source and target nodes.
152 *
153 * @param source
154 * the source node
155 * @param target
156 * the target node
157 * @param graphDataSource
158 * the graph data source
159 * @return the number of parallel edges between the two nodes
160 */
161 public static <V> int getEdgeCount(V source, V target, IGraphDataSource<V> graphDataSource) {
162 Integer count = graphDataSource.getTargetNodes(source).getCount(target);
163 if (count == null) {
164 return 0;
165 } else {
166 return count;
167 }
168 }
169}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.itc.alg.misc;
10
11import java.util.Deque;
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.rete.itc.igraph.ITcDataSource;
15
16/**
17 * The path finder provides methods for retrieving paths in a graph between a source node and one or more target nodes.
18 * Use {@link ITcDataSource#getPathFinder()} for instantiating.
19 *
20 * @author Abel Hegedus
21 *
22 * @param <V> the node type of the graph
23 */
24public interface IGraphPathFinder<V> {
25
26 /**
27 * Returns an arbitrary path from the source node to the target node (if such exists). If there is no path
28 * between them, an empty collection is returned.
29 *
30 * @param sourceNode the source node of the path
31 * @param targetNode the target node of the path
32 * @return the path from the source to the target, or empty collection if target is not reachable from source.
33 */
34 Deque<V> getPath(V sourceNode, V targetNode);
35
36 /**
37 * Returns the collection of shortest paths from the source node to the target node (if such exists). If there is no path
38 * between them, an empty collection is returned.
39 *
40 * @param sourceNode the source node of the path
41 * @param targetNode the target node of the path
42 * @return the collection of shortest paths from the source to the target, or empty collection if target is not reachable from source.
43 */
44 Iterable<Deque<V>> getShortestPaths(V sourceNode, V targetNode);
45
46 /**
47 * Returns the collection of paths from the source node to the target node (if such exists). If there is no path
48 * between them, an empty collection is returned.
49 *
50 * @param sourceNode the source node of the path
51 * @param targetNode the target node of the path
52 * @return the collection of paths from the source to the target, or empty collection if target is not reachable from source.
53 */
54 Iterable<Deque<V>> getAllPaths(V sourceNode, V targetNode);
55
56 /**
57 * Returns the collection of paths from the source node to any of the target nodes (if such exists). If there is no path
58 * between them, an empty collection is returned.
59 *
60 * @param sourceNode the source node of the path
61 * @param targetNodes the set of target nodes of the paths
62 * @return the collection of paths from the source to any of the targets, or empty collection if neither target is reachable from source.
63 */
64 Iterable<Deque<V>> getAllPathsToTargets(V sourceNode, Set<V> targetNodes);
65
66
67}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.misc;
11
12import java.util.Set;
13
14public interface ITcRelation<V> {
15
16 /**
17 * Returns the starting nodes from a transitive closure relation.
18 *
19 * @return the set of starting nodes
20 */
21 public Set<V> getTupleStarts();
22
23 /**
24 * Returns the set of nodes that are reachable from the given node.
25 *
26 * @param start
27 * the starting node
28 * @return the set of reachable nodes
29 */
30 public Set<V> getTupleEnds(V start);
31}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.misc;
11
12public class Tuple<V> {
13
14 private V source;
15 private V target;
16
17 public Tuple(V source, V target) {
18 super();
19 this.source = source;
20 this.target = target;
21 }
22
23 public V getSource() {
24 return source;
25 }
26
27 public void setSource(V source) {
28 this.source = source;
29 }
30
31 public V getTarget() {
32 return target;
33 }
34
35 public void setTarget(V target) {
36 this.target = target;
37 }
38
39 @Override
40 public String toString() {
41 return "(" + source.toString() + "," + target.toString() + ")";
42 }
43
44 @Override
45 public boolean equals(Object o) {
46 if (o instanceof Tuple) {
47 Tuple<?> t = (Tuple<?>) o;
48
49 if (t.getSource().equals(this.source) && t.getTarget().equals(this.target)) {
50 return true;
51 }
52 }
53 return false;
54 }
55
56 @Override
57 public int hashCode() {
58 return source.hashCode() + target.hashCode();
59 }
60}
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..22ce8962
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java
@@ -0,0 +1,148 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.misc.bfs;
11
12import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource;
13import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource;
14
15import java.util.*;
16
17public class BFS<V> {
18
19 private BFS() {/*Utility class constructor*/}
20
21 /**
22 * Performs a breadth first search on the given graph to determine whether source is reachable from target.
23 *
24 * @param <V>
25 * the type parameter of the nodes in the graph
26 * @param source
27 * the source node
28 * @param target
29 * the target node
30 * @param graph
31 * the graph data source
32 * @return true if source is reachable from target, false otherwise
33 */
34 public static <V> boolean isReachable(V source, V target, IGraphDataSource<V> graph) {
35 Deque<V> nodeQueue = new ArrayDeque<V>();
36 Set<V> visited = new HashSet<V>();
37
38 nodeQueue.add(source);
39 visited.add(source);
40
41 boolean ret = _isReachable(target, graph, nodeQueue, visited);
42 return ret;
43 }
44
45 private static <V> boolean _isReachable(V target, IGraphDataSource<V> graph, Deque<V> nodeQueue, Set<V> visited) {
46
47 while (!nodeQueue.isEmpty()) {
48 V node = nodeQueue.removeFirst();
49 for (V t : graph.getTargetNodes(node).distinctValues()){
50 if (t.equals(target)) {
51 return true;
52 }
53 if (!visited.contains(t)) {
54 visited.add(t);
55 nodeQueue.addLast(t);
56 }
57 }
58 }
59 return false;
60 }
61
62 public static <V> Set<V> reachableSources(IBiDirectionalGraphDataSource<V> graph, V target) {
63 Set<V> retSet = new HashSet<V>();
64 retSet.add(target);
65 Deque<V> nodeQueue = new ArrayDeque<V>();
66 nodeQueue.add(target);
67
68 _reachableSources(graph, nodeQueue, retSet);
69
70 return retSet;
71 }
72
73 private static <V> void _reachableSources(IBiDirectionalGraphDataSource<V> graph, Deque<V> nodeQueue,
74 Set<V> retSet) {
75 while (!nodeQueue.isEmpty()) {
76 V node = nodeQueue.removeFirst();
77 for (V _node : graph.getSourceNodes(node).distinctValues()) {
78 if (!retSet.contains(_node)) {
79 retSet.add(_node);
80 nodeQueue.addLast(_node);
81 }
82 }
83 }
84 }
85
86 public static <V> Set<V> reachableTargets(IGraphDataSource<V> graph, V source) {
87 Set<V> retSet = new HashSet<V>();
88 retSet.add(source);
89 Deque<V> nodeQueue = new ArrayDeque<V>();
90 nodeQueue.add(source);
91
92 _reachableTargets(graph, nodeQueue, retSet);
93
94 return retSet;
95 }
96
97 private static <V> void _reachableTargets(IGraphDataSource<V> graph, Deque<V> nodeQueue, Set<V> retSet) {
98 while (!nodeQueue.isEmpty()) {
99 V node = nodeQueue.removeFirst();
100
101 for (V _node : graph.getTargetNodes(node).distinctValues()) {
102
103 if (!retSet.contains(_node)) {
104 retSet.add(_node);
105 nodeQueue.addLast(_node);
106 }
107 }
108 }
109 }
110
111 /**
112 * Performs a breadth first search on the given graph and collects all the nodes along the path from source to
113 * target if such path exists.
114 *
115 * @param <V>
116 * the type parameter of the nodes in the graph
117 * @param source
118 * the source node
119 * @param target
120 * the target node
121 * @param graph
122 * the graph data source
123 * @return the set of nodes along the path
124 */
125 public static <V> Set<V> collectNodesAlongPath(V source, V target, IGraphDataSource<V> graph) {
126 Set<V> path = new HashSet<V>();
127 _collectNodesAlongPath(source, target, graph, path);
128 return path;
129 }
130
131 private static <V> boolean _collectNodesAlongPath(V node, V target, IGraphDataSource<V> graph, Set<V> path) {
132
133 boolean res = false;
134
135 // end recursion
136 if (node.equals(target)) {
137 path.add(node);
138 return true;
139 } else {
140 for (V _nodeT : graph.getTargetNodes(node).distinctValues()) {
141 res = (_collectNodesAlongPath(_nodeT, target, graph, path)) || res;
142 }
143 if (res)
144 path.add(node);
145 return res;
146 }
147 }
148}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.misc.scc;
11
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.List;
16import java.util.Map;
17
18import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource;
19import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalWrapper;
20import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource;
21import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphObserver;
22
23public class PKAlg<V> implements IGraphObserver<V> {
24
25 /**
26 * Maps the nodes to their indicies.
27 */
28 private Map<V, Integer> node2index;
29 private Map<Integer, V> index2node;
30 private Map<V, Boolean> node2mark;
31
32 /**
33 * Maps the index of a node to the index in the topsort.
34 */
35 private Map<Integer, Integer> index2topsort;
36 private Map<Integer, Integer> topsort2index;
37
38 /**
39 * Index associated to the inserted nodes (incrementing with every insertion).
40 */
41 private int index;
42
43 /**
44 * Index within the topsort for the target node when edge insertion occurs.
45 */
46 private int lower_bound;
47
48 /**
49 * Index within the topsort for the source node when edge insertion occurs.
50 */
51 private int upper_bound;
52
53 private List<V> RF;
54 private List<V> RB;
55 private IBiDirectionalGraphDataSource<V> gds;
56
57 public PKAlg(IGraphDataSource<V> gds) {
58 if (gds instanceof IBiDirectionalGraphDataSource<?>) {
59 this.gds = (IBiDirectionalGraphDataSource<V>) gds;
60 } else {
61 this.gds = new IBiDirectionalWrapper<V>(gds);
62 }
63
64 node2mark = new HashMap<V, Boolean>();
65 node2index = new HashMap<V, Integer>();
66 index2node = new HashMap<Integer, V>();
67 index2topsort = new HashMap<Integer, Integer>();
68 topsort2index = new HashMap<Integer, Integer>();
69 index = 0;
70
71 gds.attachObserver(this);
72 }
73
74 @Override
75 public void edgeInserted(V source, V target) {
76
77 RF = new ArrayList<V>();
78 RB = new ArrayList<V>();
79
80 lower_bound = index2topsort.get(node2index.get(target));
81 upper_bound = index2topsort.get(node2index.get(source));
82
83 if (lower_bound < upper_bound) {
84 dfsForward(target);
85 dfsBackward(source);
86 reorder();
87 }
88 }
89
90 private List<Integer> getIndicies(List<V> list) {
91 List<Integer> indicies = new ArrayList<Integer>();
92
93 for (V n : list)
94 indicies.add(index2topsort.get(node2index.get(n)));
95
96 return indicies;
97 }
98
99 private void reorder() {
100
101 Collections.reverse(RB);
102
103 // azon csomopontok indexei amelyek sorrendje nem jo
104 List<Integer> L = getIndicies(RF);
105 L.addAll(getIndicies(RB));
106 Collections.sort(L);
107
108 for (int i = 0; i < RB.size(); i++) {
109 index2topsort.put(node2index.get(RB.get(i)), L.get(i));
110 topsort2index.put(L.get(i), node2index.get(RB.get(i)));
111 }
112
113 for (int i = 0; i < RF.size(); i++) {
114 index2topsort.put(node2index.get(RF.get(i)), L.get(i + RB.size()));
115 topsort2index.put(L.get(i + RB.size()), node2index.get(RF.get(i)));
116 }
117 }
118
119 @SuppressWarnings("unused")
120 private List<V> getTopSort() {
121 List<V> topsort = new ArrayList<V>();
122
123 for (int i : topsort2index.values()) {
124 topsort.add(index2node.get(i));
125 }
126
127 return topsort;
128 }
129
130 private void dfsBackward(V node) {
131 node2mark.put(node, true);
132 RB.add(node);
133
134 for (V sn : gds.getSourceNodes(node).distinctValues()) {
135 int top_id = index2topsort.get(node2index.get(sn));
136
137 if (!node2mark.get(sn) && lower_bound < top_id)
138 dfsBackward(sn);
139 }
140 }
141
142 private void dfsForward(V node) {
143 node2mark.put(node, true);
144 RF.add(node);
145
146 for (V tn : gds.getTargetNodes(node).distinctValues()) {
147 int top_id = index2topsort.get(node2index.get(tn));
148
149 if (top_id == upper_bound)
150 System.out.println("!!!Cycle detected!!!");
151 else if (!node2mark.get(tn) && top_id < upper_bound)
152 dfsForward(tn);
153 }
154 }
155
156 @Override
157 public void edgeDeleted(V source, V target) {
158 // Edge deletion does not affect topsort
159 }
160
161 @Override
162 public void nodeInserted(V n) {
163 node2mark.put(n, false);
164 node2index.put(n, index);
165 index2node.put(index, n);
166 index2topsort.put(index, index);
167 topsort2index.put(index, index);
168 index++;
169 }
170
171 @Override
172 public void nodeDeleted(V n) {
173 node2mark.remove(n);
174 int node_id = node2index.remove(n);
175 index2node.remove(node_id);
176 int top_id = index2topsort.remove(node_id);
177 topsort2index.remove(top_id);
178 }
179}
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..de070839
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java
@@ -0,0 +1,143 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.misc.scc;
11
12import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
13import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource;
14
15import java.util.*;
16
17/**
18 * Efficient algorithms to compute the Strongly Connected Components in a directed graph.
19 *
20 * @author Tamas Szabo
21 *
22 * @param <V>
23 * the type parameter of the nodes in the graph
24 */
25public class SCC<V> {
26
27 private SCC() {/*Utility class constructor*/}
28
29 public static long sccId = 0;
30
31 /**
32 * Computes the SCCs for the given graph and returns them as a multiset. (Iterative version of Tarjan's algorithm)
33 *
34 * @param g
35 * the directed graph data source
36 * @return the set of SCCs
37 */
38 public static <V> SCCResult<V> computeSCC(IGraphDataSource<V> g) {
39 int index = 0;
40 Set<Set<V>> ret = new HashSet<Set<V>>();
41
42 // stores the lowlink and index information for the given node
43 Map<V, SCCProperty> nodeMap = CollectionsFactory.createMap();
44
45 // stores all target nodes of a given node - the list will be modified
46 Map<V, Set<V>> targetNodeMap = CollectionsFactory.createMap();
47
48 // stores those target nodes for a given node which have not been visited
49 Map<V, Set<V>> notVisitedMap = CollectionsFactory.createMap();
50
51 // stores the nodes during the traversal
52 Deque<V> nodeStack = new ArrayDeque<V>();
53
54 // stores the nodes which belong to an scc (there can be many sccs in the stack at the same time)
55 Deque<V> sccStack = new ArrayDeque<V>();
56
57 boolean sink = false, finishedTraversal = true;
58
59 // initialize all nodes with 0 index and 0 lowlink
60 Set<V> allNodes = g.getAllNodes();
61 for (V n : allNodes) {
62 nodeMap.put(n, new SCCProperty(0, 0));
63 }
64
65 for (V n : allNodes) {
66 // if the node has not been visited yet
67 if (nodeMap.get(n).getIndex() == 0) {
68 nodeStack.push(n);
69
70 while (!nodeStack.isEmpty()) {
71 V currentNode = nodeStack.peekLast();
72 sink = false;
73 finishedTraversal = false;
74 SCCProperty prop = nodeMap.get(currentNode);
75
76 if (nodeMap.get(currentNode).getIndex() == 0) {
77 index++;
78 sccStack.addLast(currentNode);
79 prop.setIndex(index);
80 prop.setLowlink(index);
81
82 notVisitedMap.put(currentNode, new HashSet<V>());
83
84 // storing the target nodes of the actual node
85 if (g.getTargetNodes(currentNode) != null) {
86 Set<V> targets = g.getTargetNodes(currentNode).distinctValues();
87 targetNodeMap.put(currentNode, CollectionsFactory.createSet(targets));
88 }
89 }
90
91 if (targetNodeMap.get(currentNode) != null) {
92
93 // remove node from stack, the exploration of its children has finished
94 if (targetNodeMap.get(currentNode).size() == 0) {
95 targetNodeMap.remove(currentNode);
96
97 nodeStack.removeLast();
98
99 for (V targetNode : g.getTargetNodes(currentNode).distinctValues()) {
100 if (notVisitedMap.get(currentNode).contains(targetNode)) {
101 prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getLowlink()));
102 } else if (sccStack.contains(targetNode)) {
103 prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getIndex()));
104 }
105 }
106
107 finishedTraversal = true;
108 } else {
109 V targetNode = targetNodeMap.get(currentNode).iterator().next();
110 targetNodeMap.get(currentNode).remove(targetNode);
111 // if the targetNode has not yet been visited push it to the stack
112 // and mark it in the notVisitedMap
113 if (nodeMap.get(targetNode).getIndex() == 0) {
114 notVisitedMap.get(currentNode).add(targetNode);
115 nodeStack.addLast(targetNode);
116 }
117 }
118 }
119 // if currentNode has no target nodes
120 else {
121 nodeStack.removeLast();
122 sink = true;
123 }
124
125 // create scc if node is a sink or an scc has been found
126 if ((sink || finishedTraversal) && (prop.getLowlink() == prop.getIndex())) {
127 Set<V> sc = new HashSet<V>();
128 V targetNode = null;
129
130 do {
131 targetNode = sccStack.removeLast();
132 sc.add(targetNode);
133 } while (!targetNode.equals(currentNode));
134
135 ret.add(sc);
136 }
137 }
138 }
139 }
140
141 return new SCCResult<V>(ret, g);
142 }
143}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.misc.scc;
11
12public class SCCProperty {
13 private int index;
14 private int lowlink;
15
16 public SCCProperty(int index, int lowlink) {
17 super();
18 this.index = index;
19 this.lowlink = lowlink;
20 }
21
22 public int getIndex() {
23 return index;
24 }
25
26 public void setIndex(int index) {
27 this.index = index;
28 }
29
30 public int getLowlink() {
31 return lowlink;
32 }
33
34 public void setLowlink(int lowlink) {
35 this.lowlink = lowlink;
36 }
37}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.misc.scc;
11
12import java.util.Map.Entry;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource;
16
17public class SCCResult<V> {
18
19 private Set<Set<V>> sccs;
20 private IGraphDataSource<V> gds;
21
22 public SCCResult(Set<Set<V>> sccs, IGraphDataSource<V> gds) {
23 this.sccs = sccs;
24 this.gds = gds;
25 }
26
27 public Set<Set<V>> getSccs() {
28 return sccs;
29 }
30
31 public int getSCCCount() {
32 return sccs.size();
33 }
34
35 public double getAverageNodeCount() {
36 double a = 0;
37
38 for (Set<V> s : sccs) {
39 a += s.size();
40 }
41
42 return a / sccs.size();
43 }
44
45 public double getAverageEdgeCount() {
46 long edgeSum = 0;
47
48 for (Set<V> scc : sccs) {
49 for (V source : scc) {
50 for (Entry<V, Integer> entry : gds.getTargetNodes(source).entriesWithMultiplicities()) {
51 if (scc.contains(entry.getKey())) {
52 edgeSum += entry.getValue();
53 }
54 }
55 }
56 }
57
58 return (double) edgeSum / (double) sccs.size();
59 }
60
61 public int getBiggestSCCSize() {
62 int max = 0;
63
64 for (Set<V> scc : sccs) {
65 if (scc.size() > max)
66 max = scc.size();
67 }
68
69 return max;
70 }
71
72 public long getSumOfSquares() {
73 long sum = 0;
74
75 for (Set<V> scc : sccs) {
76 sum += scc.size() * scc.size();
77 }
78
79 return sum;
80 }
81}
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..89be6804
--- /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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.alg.misc.topsort;
11
12import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource;
13
14import java.util.*;
15
16/**
17 * @since 1.6
18 */
19public class TopologicalSorting {
20
21 private TopologicalSorting() {/*Utility class constructor*/}
22
23 private static final class Pair<T> {
24 public T element;
25 public boolean isParent;
26
27 public Pair(final T element, final boolean isParent) {
28 this.element = element;
29 this.isParent = isParent;
30 }
31 }
32
33 /**
34 * Returns a topological ordering for the given graph data source.
35 * Output format: if there is an a -> b (transitive) reachability, then node <code>a</code> will come before node <code>b</code> in the resulting list.
36 *
37 * @param gds the graph data source
38 * @return a topological ordering
39 */
40 public static <T> List<T> compute(final IGraphDataSource<T> gds) {
41 final Set<T> visited = new HashSet<T>();
42 final LinkedList<T> result = new LinkedList<T>();
43 final Deque<Pair<T>> dfsStack = new ArrayDeque<Pair<T>>();
44
45 for (final T node : gds.getAllNodes()) {
46 if (!visited.contains(node)) {
47 dfsStack.addLast(new Pair<T>(node, false));
48 }
49
50 while (!dfsStack.isEmpty()) {
51 final Pair<T> head = dfsStack.removeLast();
52 final T source = head.element;
53
54 if (head.isParent) {
55 // we have already seen source, push it to the resulting stack
56 result.addFirst(source);
57 } else {
58 // first time we see source, continue with its children
59 visited.add(source);
60 dfsStack.addLast(new Pair<T>(source, true));
61
62 for (final T target : gds.getTargetNodes(source).distinctValues()) {
63 if (!visited.contains(target)) {
64 dfsStack.addLast(new Pair<T>(target, false));
65 }
66 }
67 }
68 }
69 }
70
71 return result;
72 }
73}
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..794dabc0
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java
@@ -0,0 +1,174 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.viatra.runtime.rete.itc.alg.representative;
7
8import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph;
9import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphObserver;
10import tools.refinery.viatra.runtime.matchers.util.Direction;
11
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.Map;
15import java.util.Set;
16
17public abstract class RepresentativeElectionAlgorithm implements IGraphObserver<Object> {
18 protected final Graph<Object> graph;
19 protected final Map<Object, Object> representatives = new HashMap<>();
20 protected final Map<Object, Set<Object>> components = new HashMap<>();
21 private RepresentativeObserver observer;
22
23 protected RepresentativeElectionAlgorithm(Graph<Object> graph) {
24 this.graph = graph;
25 initializeComponents();
26 graph.attachObserver(this);
27 }
28
29 protected abstract void initializeComponents();
30
31 protected void initializeSet(Set<Object> set) {
32 var iterator = set.iterator();
33 if (!iterator.hasNext()) {
34 // Set is empty.
35 return;
36 }
37 var representative = iterator.next();
38 for (var node : set) {
39 var oldRepresentative = representatives.put(node, representative);
40 if (oldRepresentative != null && !representative.equals(oldRepresentative)) {
41 throw new IllegalStateException("Node %s is already in a set represented by %s, cannot add it to %s"
42 .formatted(node, oldRepresentative, set));
43 }
44 }
45 components.put(representative, set);
46 }
47
48 protected void merge(Set<Object> toMerge) {
49 if (toMerge.isEmpty()) {
50 return;
51 }
52 var representativesToMerge = new HashSet<>();
53 Object bestRepresentative = null;
54 Set<Object> bestSet = null;
55 for (var object : toMerge) {
56 var representative = getRepresentative(object);
57 if (representativesToMerge.add(representative)) {
58 var component = getComponent(representative);
59 if (bestSet == null || bestSet.size() < component.size()) {
60 bestRepresentative = representative;
61 bestSet = component;
62 }
63 }
64 }
65 if (bestRepresentative == null) {
66 throw new AssertionError("Could not determine best representative");
67 }
68 for (var representative : representativesToMerge) {
69 if (!bestRepresentative.equals(representative)) {
70 components.remove(representative);
71 }
72 }
73 components.put(bestRepresentative, toMerge);
74 for (var object : toMerge) {
75 var previousRepresentative = representatives.put(object, bestRepresentative);
76 if (!bestSet.contains(object)) {
77 notifyToObservers(object, previousRepresentative, bestRepresentative);
78 }
79 }
80 }
81
82 protected void merge(Object leftRepresentative, Object rightRepresentative) {
83 if (leftRepresentative.equals(rightRepresentative)) {
84 return;
85 }
86 var leftSet = getComponent(leftRepresentative);
87 var rightSet = getComponent(rightRepresentative);
88 if (leftSet.size() < rightSet.size()) {
89 merge(rightRepresentative, rightSet, leftRepresentative, leftSet);
90 } else {
91 merge(leftRepresentative, leftSet, rightRepresentative, rightSet);
92 }
93 }
94
95 private void merge(Object preservedRepresentative, Set<Object> preservedSet, Object removedRepresentative,
96 Set<Object> removedSet) {
97 components.remove(removedRepresentative);
98 for (var node : removedSet) {
99 representatives.put(node, preservedRepresentative);
100 preservedSet.add(node);
101 notifyToObservers(node, removedRepresentative, preservedRepresentative);
102 }
103 }
104
105 protected void assignNewRepresentative(Object oldRepresentative, Set<Object> set) {
106 var iterator = set.iterator();
107 if (!iterator.hasNext()) {
108 return;
109 }
110 var newRepresentative = iterator.next();
111 components.put(newRepresentative, set);
112 for (var node : set) {
113 var oldRepresentativeOfNode = representatives.put(node, newRepresentative);
114 if (!oldRepresentative.equals(oldRepresentativeOfNode)) {
115 throw new IllegalArgumentException("Node %s was not represented by %s but by %s"
116 .formatted(node, oldRepresentative, oldRepresentativeOfNode));
117 }
118 notifyToObservers(node, oldRepresentative, newRepresentative);
119 }
120 }
121
122 public void setObserver(RepresentativeObserver observer) {
123 this.observer = observer;
124 }
125
126 public Map<Object, Set<Object>> getComponents() {
127 return components;
128 }
129
130 public Object getRepresentative(Object node) {
131 return representatives.get(node);
132 }
133
134 public Set<Object> getComponent(Object representative) {
135 return components.get(representative);
136 }
137
138 public void dispose() {
139 graph.detachObserver(this);
140 }
141
142 @Override
143 public void nodeInserted(Object n) {
144 var component = new HashSet<>(1);
145 component.add(n);
146 initializeSet(component);
147 notifyToObservers(n, n, Direction.INSERT);
148 }
149
150 @Override
151 public void nodeDeleted(Object n) {
152 var representative = representatives.remove(n);
153 if (!representative.equals(n)) {
154 throw new IllegalStateException("Trying to delete node with dangling edges");
155 }
156 components.remove(representative);
157 notifyToObservers(n, representative, Direction.DELETE);
158 }
159
160 protected void notifyToObservers(Object node, Object oldRepresentative, Object newRepresentative) {
161 notifyToObservers(node, oldRepresentative, Direction.DELETE);
162 notifyToObservers(node, newRepresentative, Direction.INSERT);
163 }
164
165 protected void notifyToObservers(Object node, Object representative, Direction direction) {
166 if (observer != null) {
167 observer.tupleChanged(node, representative, direction);
168 }
169 }
170
171 public interface Factory {
172 RepresentativeElectionAlgorithm create(Graph<Object> graph);
173 }
174}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.viatra.runtime.rete.itc.alg.representative;
7
8import tools.refinery.viatra.runtime.matchers.util.Direction;
9
10public interface RepresentativeObserver {
11 void tupleChanged(Object node, Object representative, Direction direction);
12}
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..0463301b
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java
@@ -0,0 +1,69 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.viatra.runtime.rete.itc.alg.representative;
7
8import tools.refinery.viatra.runtime.rete.itc.alg.misc.GraphHelper;
9import tools.refinery.viatra.runtime.rete.itc.alg.misc.bfs.BFS;
10import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCC;
11import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph;
12
13import java.util.Collection;
14import java.util.Set;
15
16public class StronglyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm {
17 public StronglyConnectedComponentAlgorithm(Graph<Object> graph) {
18 super(graph);
19 }
20
21 @Override
22 protected void initializeComponents() {
23 var computedSCCs = SCC.computeSCC(graph).getSccs();
24 for (var computedSCC : computedSCCs) {
25 initializeSet(computedSCC);
26 }
27 }
28
29 @Override
30 public void edgeInserted(Object source, Object target) {
31 var sourceRoot = getRepresentative(source);
32 var targetRoot = getRepresentative(target);
33 if (sourceRoot.equals(targetRoot)) {
34 // New edge does not change strongly connected components.
35 return;
36 }
37 if (BFS.isReachable(target, source, graph)) {
38 var sources = BFS.reachableSources(graph, target);
39 var targets = BFS.reachableTargets(graph, source);
40 sources.retainAll(targets);
41 merge(sources);
42 }
43 }
44
45 @Override
46 public void edgeDeleted(Object source, Object target) {
47 var sourceRoot = getRepresentative(source);
48 var targetRoot = getRepresentative(target);
49 if (!sourceRoot.equals(targetRoot)) {
50 // New edge does not change strongly connected components.
51 return;
52 }
53 var component = GraphHelper.getSubGraph(getComponent(sourceRoot), graph);
54 if (!BFS.isReachable(source, target, component)) {
55 var newSCCs = SCC.computeSCC(component).getSccs();
56 split(sourceRoot, newSCCs);
57 }
58 }
59
60 private void split(Object preservedRepresentative, Collection<? extends Set<Object>> sets) {
61 for (var set : sets) {
62 if (set.contains(preservedRepresentative)) {
63 components.put(preservedRepresentative, set);
64 } else {
65 assignNewRepresentative(preservedRepresentative, set);
66 }
67 }
68 }
69}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.viatra.runtime.rete.itc.alg.representative;
7
8import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph;
9
10import java.util.ArrayDeque;
11import java.util.HashSet;
12import java.util.Set;
13
14public class WeaklyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm {
15 public WeaklyConnectedComponentAlgorithm(Graph<Object> graph) {
16 super(graph);
17 }
18
19 @Override
20 protected void initializeComponents() {
21 for (var node : graph.getAllNodes()) {
22 if (representatives.containsKey(node)) {
23 continue;
24 }
25 var reachable = getReachableNodes(node);
26 initializeSet(reachable);
27 }
28 }
29
30 @Override
31 public void edgeInserted(Object source, Object target) {
32 var sourceRoot = getRepresentative(source);
33 var targetRoot = getRepresentative(target);
34 merge(sourceRoot, targetRoot);
35 }
36
37 @Override
38 public void edgeDeleted(Object source, Object target) {
39 var sourceRoot = getRepresentative(source);
40 var targetRoot = getRepresentative(target);
41 if (!sourceRoot.equals(targetRoot)) {
42 throw new IllegalArgumentException("Trying to remove edge not in graph");
43 }
44 var targetReachable = getReachableNodes(target);
45 if (!targetReachable.contains(source)) {
46 split(sourceRoot, targetReachable);
47 }
48 }
49
50 private void split(Object sourceRepresentative, Set<Object> targetReachable) {
51 var sourceComponent = getComponent(sourceRepresentative);
52 sourceComponent.removeAll(targetReachable);
53 if (targetReachable.contains(sourceRepresentative)) {
54 components.put(sourceRepresentative, targetReachable);
55 assignNewRepresentative(sourceRepresentative, sourceComponent);
56 } else {
57 assignNewRepresentative(sourceRepresentative, targetReachable);
58 }
59 }
60
61 private Set<Object> getReachableNodes(Object source) {
62 var retSet = new HashSet<>();
63 retSet.add(source);
64 var nodeQueue = new ArrayDeque<>();
65 nodeQueue.addLast(source);
66
67 while (!nodeQueue.isEmpty()) {
68 var node = nodeQueue.removeFirst();
69 for (var neighbor : graph.getTargetNodes(node).distinctValues()) {
70 if (!retSet.contains(neighbor)) {
71 retSet.add(neighbor);
72 nodeQueue.addLast(neighbor);
73 }
74 }
75 for (var neighbor : graph.getSourceNodes(node).distinctValues()) {
76 if (!retSet.contains(neighbor)) {
77 retSet.add(neighbor);
78 nodeQueue.addLast(neighbor);
79 }
80 }
81 }
82
83 return retSet;
84 }
85}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.itc.alg.util;
10
11import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
12
13import java.util.Set;
14
15/**
16 * @author Tamas Szabo
17 *
18 */
19public class CollectionHelper {
20
21 private CollectionHelper() {/*Utility class constructor*/}
22
23 /**
24 * Returns the intersection of two sets. It calls {@link Set#retainAll(java.util.Collection)} but returns a new set
25 * containing the elements of the intersection.
26 *
27 * @param set1
28 * the first set (can be null, interpreted as empty)
29 * @param set2
30 * the second set (can be null, interpreted as empty)
31 * @return the intersection of the sets
32 * @since 1.7
33 */
34 public static <V> Set<V> intersection(Set<V> set1, Set<V> set2) {
35 if (set1 == null || set2 == null)
36 return CollectionsFactory.createSet();
37
38 Set<V> intersection = CollectionsFactory.createSet(set1);
39 intersection.retainAll(set2);
40 return intersection;
41 }
42
43
44 /**
45 * Returns the difference of two sets (S1\S2). It calls {@link Set#removeAll(java.util.Collection)} but returns a
46 * new set containing the elements of the difference.
47 *
48 * @param set1
49 * the first set (can be null, interpreted as empty)
50 * @param set2
51 * the second set (can be null, interpreted as empty)
52 * @return the difference of the sets
53 * @since 1.7
54 */
55 public static <V> Set<V> difference(Set<V> set1, Set<V> set2) {
56 if (set1 == null)
57 return CollectionsFactory.createSet();
58
59 Set<V> difference = CollectionsFactory.createSet(set1);
60 if (set2 != null) difference.removeAll(set2);
61 return difference;
62 }
63
64}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.itc.graphimpl;
10
11import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCC;
12import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCCResult;
13import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
14
15import java.util.HashMap;
16import java.util.Map;
17import java.util.Set;
18import java.util.function.Function;
19
20/**
21 * This class contains utility methods to generate dot representations for {@link Graph} instances.
22 *
23 * @author Tamas Szabo
24 * @since 2.3
25 */
26public class DotGenerator {
27
28 private static final String[] colors = new String[] { "yellow", "blue", "red", "green", "gray", "cyan" };
29
30 private DotGenerator() {
31
32 }
33
34 /**
35 * Generates the dot representation for the given graph.
36 *
37 * @param graph
38 * the graph
39 * @param colorSCCs
40 * specifies if the strongly connected components with size greater than shall be colored
41 * @param nameFunction
42 * use this function to provide custom names to nodes, null if the default toString shall be used
43 * @param colorFunction
44 * use this function to provide custom color to nodes, null if the default white color shall be used
45 * @param edgeFunction
46 * use this function to provide custom edge labels, null if no edge label shall be printed
47 * @return the dot representation as a string
48 */
49 public static <V> String generateDot(final Graph<V> graph, final boolean colorSCCs,
50 final Function<V, String> nameFunction, final Function<V, String> colorFunction,
51 final Function<V, Function<V, String>> edgeFunction) {
52 final Map<V, String> colorMap = new HashMap<V, String>();
53
54 if (colorSCCs) {
55 final SCCResult<V> result = SCC.computeSCC(graph);
56 final Set<Set<V>> sccs = result.getSccs();
57
58 int i = 0;
59 for (final Set<V> scc : sccs) {
60 if (scc.size() > 1) {
61 for (final V node : scc) {
62 final String color = colorMap.get(node);
63 if (color == null) {
64 colorMap.put(node, colors[i % colors.length]);
65 } else {
66 colorMap.put(node, colorMap.get(node) + ":" + colors[i % colors.length]);
67 }
68 }
69 i++;
70 }
71 }
72
73 // if a node has no color yet, then make it white
74 for (final V node : graph.getAllNodes()) {
75 if (!colorMap.containsKey(node)) {
76 colorMap.put(node, "white");
77 }
78 }
79 } else {
80 for (final V node : graph.getAllNodes()) {
81 colorMap.put(node, "white");
82 }
83 }
84
85 if (colorFunction != null) {
86 for (final V node : graph.getAllNodes()) {
87 colorMap.put(node, colorFunction.apply(node));
88 }
89 }
90
91 final StringBuilder builder = new StringBuilder();
92 builder.append("digraph g {\n");
93
94 for (final V node : graph.getAllNodes()) {
95 final String nodePresentation = nameFunction == null ? node.toString() : nameFunction.apply(node);
96 builder.append("\"" + nodePresentation + "\"");
97 builder.append("[style=filled,fillcolor=" + colorMap.get(node) + "]");
98 builder.append(";\n");
99 }
100
101 for (final V source : graph.getAllNodes()) {
102 final IMemoryView<V> targets = graph.getTargetNodes(source);
103 if (!targets.isEmpty()) {
104 final String sourcePresentation = nameFunction == null ? source.toString() : nameFunction.apply(source);
105 for (final V target : targets.distinctValues()) {
106 String edgeLabel = null;
107 if (edgeFunction != null) {
108 final Function<V, String> v1 = edgeFunction.apply(source);
109 if (v1 != null) {
110 edgeLabel = v1.apply(target);
111 }
112 }
113
114 final String targetPresentation = nameFunction == null ? target.toString()
115 : nameFunction.apply(target);
116
117 builder.append("\"" + sourcePresentation + "\" -> \"" + targetPresentation + "\"");
118 if (edgeLabel != null) {
119 builder.append("[label=\"" + edgeLabel + "\"]");
120 }
121 builder.append(";\n");
122 }
123 }
124 }
125
126 builder.append("}");
127 return builder.toString();
128 }
129
130 /**
131 * Generates the dot representation for the given graph. No special pretty printing customization will be applied.
132 *
133 * @param graph
134 * the graph
135 * @return the dot representation as a string
136 */
137 public static <V> String generateDot(final Graph<V> graph) {
138 return generateDot(graph, false, null, null, null);
139 }
140
141 /**
142 * Returns a simple name shortener function that can be used in the graphviz visualization to help with readability.
143 * WARNING: if you shorten the name of the {@link Node}s too much, the visualization may become incorrect because
144 * grahpviz will treat different nodes as the same if their shortened names are the same.
145 *
146 * @param maxLength
147 * the maximum length of the text that is kept from the toString of the objects in the graph
148 * @return the shrunk toString value
149 */
150 public static <V> Function<V, String> getNameShortener(final int maxLength) {
151 return new Function<V, String>() {
152 @Override
153 public String apply(final V obj) {
154 final String value = obj.toString();
155 return value.substring(0, Math.min(value.length(), maxLength));
156 }
157 };
158 }
159
160}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.graphimpl;
11
12import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource;
13import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphObserver;
14import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource;
15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
16import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
17import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
18import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
19
20import java.util.List;
21import java.util.Map;
22import java.util.Map.Entry;
23import java.util.Set;
24
25public class Graph<V> implements IGraphDataSource<V>, IBiDirectionalGraphDataSource<V> {
26
27 // source -> target -> count
28 private IMultiLookup<V, V> outgoingEdges;
29 // target -> source -> count
30 private IMultiLookup<V, V> incomingEdges;
31
32 private Set<V> nodes;
33
34 private List<IGraphObserver<V>> observers;
35
36 public Graph() {
37 outgoingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class);
38 incomingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class);
39 nodes = CollectionsFactory.createSet();
40 observers = CollectionsFactory.createObserverList();
41 }
42
43 public void insertEdge(V source, V target) {
44 outgoingEdges.addPair(source, target);
45 incomingEdges.addPair(target, source);
46
47 for (IGraphObserver<V> go : observers) {
48 go.edgeInserted(source, target);
49 }
50 }
51
52 /**
53 * No-op if trying to delete edge that does not exist
54 *
55 * @since 2.0
56 * @see #deleteEdgeIfExists(Object, Object)
57 */
58 public void deleteEdgeIfExists(V source, V target) {
59 boolean containedEdge = outgoingEdges.lookupOrEmpty(source).containsNonZero(target);
60 if (containedEdge) {
61 deleteEdgeThatExists(source, target);
62 }
63 }
64
65 /**
66 * @throws IllegalStateException
67 * if trying to delete edge that does not exist
68 * @since 2.0
69 * @see #deleteEdgeIfExists(Object, Object)
70 */
71 public void deleteEdgeThatExists(V source, V target) {
72 outgoingEdges.removePair(source, target);
73 incomingEdges.removePair(target, source);
74 for (IGraphObserver<V> go : observers) {
75 go.edgeDeleted(source, target);
76 }
77 }
78
79 /**
80 * @deprecated use explicitly {@link #deleteEdgeThatExists(Object, Object)} or
81 * {@link #deleteEdgeIfExists(Object, Object)} instead. To preserve backwards compatibility, this method
82 * delegates to the latter.
83 *
84 */
85 @Deprecated
86 public void deleteEdge(V source, V target) {
87 deleteEdgeIfExists(source, target);
88 }
89
90 /**
91 * Insert the given node into the graph.
92 */
93 public void insertNode(V node) {
94 if (nodes.add(node)) {
95 for (IGraphObserver<V> go : observers) {
96 go.nodeInserted(node);
97 }
98 }
99 }
100
101 /**
102 * Deletes the given node AND all of the edges going in and out from the node.
103 */
104 public void deleteNode(V node) {
105 if (nodes.remove(node)) {
106 IMemoryView<V> incomingView = incomingEdges.lookup(node);
107 if (incomingView != null) {
108 Map<V, Integer> incoming = CollectionsFactory.createMap(incomingView.asMap());
109
110 for (Entry<V, Integer> entry : incoming.entrySet()) {
111 for (int i = 0; i < entry.getValue(); i++) {
112 deleteEdgeThatExists(entry.getKey(), node);
113 }
114 }
115 }
116
117 IMemoryView<V> outgoingView = outgoingEdges.lookup(node);
118 if (outgoingView != null) {
119 Map<V, Integer> outgoing = CollectionsFactory.createMap(outgoingView.asMap());
120
121 for (Entry<V, Integer> entry : outgoing.entrySet()) {
122 for (int i = 0; i < entry.getValue(); i++) {
123 deleteEdgeThatExists(node, entry.getKey());
124 }
125 }
126 }
127
128 for (IGraphObserver<V> go : observers) {
129 go.nodeDeleted(node);
130 }
131 }
132 }
133
134 @Override
135 public void attachObserver(IGraphObserver<V> go) {
136 observers.add(go);
137 }
138
139 @Override
140 public void attachAsFirstObserver(IGraphObserver<V> observer) {
141 observers.add(0, observer);
142 }
143
144 @Override
145 public void detachObserver(IGraphObserver<V> go) {
146 observers.remove(go);
147 }
148
149 @Override
150 public Set<V> getAllNodes() {
151 return nodes;
152 }
153
154 @Override
155 public IMemoryView<V> getTargetNodes(V source) {
156 return outgoingEdges.lookupOrEmpty(source);
157 }
158
159 @Override
160 public IMemoryView<V> getSourceNodes(V target) {
161 return incomingEdges.lookupOrEmpty(target);
162 }
163
164 @Override
165 public String toString() {
166 StringBuilder sb = new StringBuilder();
167 sb.append("nodes = ");
168 for (V n : getAllNodes()) {
169 sb.append(n.toString());
170 sb.append(" ");
171 }
172 sb.append(" edges = ");
173 for (V source : outgoingEdges.distinctKeys()) {
174 IMemoryView<V> targets = outgoingEdges.lookup(source);
175 for (V target : targets.distinctValues()) {
176 int count = targets.getCount(target);
177 for (int i = 0; i < count; i++) {
178 sb.append("(" + source + "," + target + ") ");
179 }
180 }
181 }
182 return sb.toString();
183 }
184
185}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.igraph;
11
12import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
13import tools.refinery.viatra.runtime.matchers.util.IMultiset;
14
15/**
16 * A bi-directional graph data source supports all operations that an {@link IGraphDataSource} does, but it
17 * also makes it possible to query the incoming edges of nodes, not only the outgoing edges.
18 *
19 * @author Tamas Szabo
20 *
21 * @param <V> the type of the nodes in the graph
22 */
23public interface IBiDirectionalGraphDataSource<V> extends IGraphDataSource<V> {
24
25 /**
26 * Returns the source nodes for the given target node.
27 * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source.
28 *
29 * The method must not return null.
30 *
31 * @param target the target node
32 * @return the multiset of source nodes
33 * @since 2.0
34 */
35 public IMemoryView<V> getSourceNodes(V target);
36
37}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.igraph;
11
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType;
16import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
17import tools.refinery.viatra.runtime.matchers.util.IMultiLookup;
18
19/**
20 * This class can be used to wrap an {@link IGraphDataSource} into an {@link IBiDirectionalGraphDataSource}. This class
21 * provides support for the retrieval of source nodes for a given target which is not supported by standard
22 * {@link IGraphDataSource} implementations.
23 *
24 * @author Tamas Szabo
25 *
26 * @param <V>
27 * the type parameter of the nodes in the graph data source
28 */
29public class IBiDirectionalWrapper<V> implements IBiDirectionalGraphDataSource<V>, IGraphObserver<V> {
30
31 private IGraphDataSource<V> wrappedDataSource;
32 // target -> source -> count
33 private IMultiLookup<V, V> incomingEdges;
34
35 public IBiDirectionalWrapper(IGraphDataSource<V> gds) {
36 this.wrappedDataSource = gds;
37
38 this.incomingEdges = CollectionsFactory.createMultiLookup(
39 Object.class, MemoryType.MULTISETS, Object.class);
40
41 if (gds.getAllNodes() != null) {
42 for (V source : gds.getAllNodes()) {
43 IMemoryView<V> targets = gds.getTargetNodes(source);
44 for (V target : targets.distinctValues()) {
45 int count = targets.getCount(target);
46 for (int i = 0; i < count; i++) {
47 edgeInserted(source, target);
48 }
49 }
50 }
51 }
52
53 gds.attachAsFirstObserver(this);
54 }
55
56 @Override
57 public void attachObserver(IGraphObserver<V> observer) {
58 wrappedDataSource.attachObserver(observer);
59 }
60
61 @Override
62 public void attachAsFirstObserver(IGraphObserver<V> observer) {
63 wrappedDataSource.attachAsFirstObserver(observer);
64 }
65
66 @Override
67 public void detachObserver(IGraphObserver<V> observer) {
68 wrappedDataSource.detachObserver(observer);
69 }
70
71 @Override
72 public Set<V> getAllNodes() {
73 return wrappedDataSource.getAllNodes();
74 }
75
76 @Override
77 public IMemoryView<V> getTargetNodes(V source) {
78 return wrappedDataSource.getTargetNodes(source);
79 }
80
81 @Override
82 public IMemoryView<V> getSourceNodes(V target) {
83 return incomingEdges.lookupOrEmpty(target);
84 }
85
86 @Override
87 public void edgeInserted(V source, V target) {
88 incomingEdges.addPair(target, source);
89 }
90
91 @Override
92 public void edgeDeleted(V source, V target) {
93 incomingEdges.removePair(target, source);
94 }
95
96 @Override
97 public void nodeInserted(V n) {
98
99 }
100
101 @Override
102 public void nodeDeleted(V node) {
103
104 }
105
106 @Override
107 public String toString() {
108 return wrappedDataSource.toString();
109 }
110}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.igraph;
11
12import tools.refinery.viatra.runtime.matchers.util.IMemoryView;
13import tools.refinery.viatra.runtime.matchers.util.IMultiset;
14
15import java.util.Set;
16
17/**
18 * The interface prescribes the set of operations that a graph data source must support.
19 * <p> Note that the old version of the interface is broken at version 1.6;
20 * MultiSets are now presented as Maps instead of Lists.
21 *
22 * @author Tamas Szabo
23 *
24 * @param <V>
25 * the type of the nodes in the graph
26 */
27public interface IGraphDataSource<V> {
28
29 /**
30 * Attaches a new graph observer to this graph data source. Observers will be notified in the order they have been registered.
31 *
32 * @param observer the graph observer
33 */
34 public void attachObserver(IGraphObserver<V> observer);
35
36 /**
37 * Attaches a new graph observer to this graph data source as the first one.
38 * In the notification order this observer will be the first one as long as another call to this method happens.
39 *
40 * @param observer the graph observer
41 * @since 1.6
42 */
43 public void attachAsFirstObserver(IGraphObserver<V> observer);
44
45 /**
46 * Detaches an already registered graph observer from this graph data source.
47 *
48 * @param observer the graph observer
49 */
50 public void detachObserver(IGraphObserver<V> observer);
51
52 /**
53 * Returns the complete set of nodes in the graph data source.
54 *
55 * @return the set of all nodes
56 */
57 public Set<V> getAllNodes();
58
59 /**
60 * Returns the target nodes for the given source node.
61 * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source.
62 *
63 * The method must not return null.
64 *
65 * @param source the source node
66 * @return the multiset of target nodes
67 * @since 2.0
68 */
69 public IMemoryView<V> getTargetNodes(V source);
70}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.igraph;
11
12/**
13 * Interface GraphObserver is used to observ the changes in a graph; edge and node insertion/deleteion.
14 *
15 * @author Tamas Szabo
16 *
17 */
18public interface IGraphObserver<V> {
19
20 /**
21 * Used to notify when an edge is inserted into the graph.
22 *
23 * @param source
24 * the source of the edge
25 * @param target
26 * the target of the edge
27 */
28 public void edgeInserted(V source, V target);
29
30 /**
31 * Used to notify when an edge is deleted from the graph.
32 *
33 * @param source
34 * the source of the edge
35 * @param target
36 * the target of the edge
37 */
38 public void edgeDeleted(V source, V target);
39
40 /**
41 * Used to notify when a node is inserted into the graph.
42 *
43 * @param n
44 * the node
45 */
46 public void nodeInserted(V n);
47
48 /**
49 * Used to notify when a node is deleted from the graph.
50 *
51 * @param n
52 * the node
53 */
54 public void nodeDeleted(V n);
55}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.igraph;
11
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.rete.itc.alg.misc.IGraphPathFinder;
15
16/**
17 * This interface defines those methods that a transitive reachability data source should provide.
18 *
19 * @author Tamas Szabo
20 *
21 * @param <V>
22 * the type parameter of the node
23 */
24public interface ITcDataSource<V> {
25
26 /**
27 * Attach a transitive closure relation observer.
28 *
29 * @param to
30 * the observer object
31 */
32 public void attachObserver(ITcObserver<V> to);
33
34 /**
35 * Detach a transitive closure relation observer.
36 *
37 * @param to
38 * the observer object
39 */
40 public void detachObserver(ITcObserver<V> to);
41
42 /**
43 * Returns all nodes which are reachable from the source node.
44 *
45 * @param source
46 * the source node
47 * @return the set of target nodes
48 */
49 public Set<V> getAllReachableTargets(V source);
50
51 /**
52 * Returns all nodes from which the target node is reachable.
53 *
54 * @param target
55 * the target node
56 * @return the set of source nodes
57 */
58 public Set<V> getAllReachableSources(V target);
59
60 /**
61 * Returns true if the target node is reachable from the source node.
62 *
63 * @param source
64 * the source node
65 * @param target
66 * the target node
67 * @return true if target is reachable from source, false otherwise
68 */
69 public boolean isReachable(V source, V target);
70
71 /**
72 * The returned {@link IGraphPathFinder} can be used to retrieve paths between nodes using transitive reachability.
73 *
74 * @return a path finder for the graph.
75 */
76 public IGraphPathFinder<V> getPathFinder();
77
78 /**
79 * Call this method to properly dispose the data structures of a transitive closure algorithm.
80 */
81 public void dispose();
82}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.itc.igraph;
11
12/**
13 * Interface ITcObserver is used to observe the changes in a transitive closure relation; tuple insertion/deletion.
14 *
15 * @author Szabo Tamas
16 *
17 */
18public interface ITcObserver<V> {
19
20 /**
21 * Used to notify when a tuple is inserted into the transitive closure relation.
22 *
23 * @param source
24 * the source of the tuple
25 * @param target
26 * the target of the tuple
27 */
28 public void tupleInserted(V source, V target);
29
30 /**
31 * Used to notify when a tuple is deleted from the transitive closure relation.
32 *
33 * @param source
34 * the source of the tuple
35 * @param target
36 * the target of the tuple
37 */
38 public void tupleDeleted(V source, V target);
39}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.matcher;
10
11import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
12import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
13
14/**
15 * A {@link ReteBackendFactory} implementation that creates {@link ReteEngine}s that use delete and re-derive
16 * evaluation.
17 *
18 * @author Tamas Szabo
19 * @since 2.2
20 */
21public class DRedReteBackendFactory extends ReteBackendFactory {
22
23 public static final DRedReteBackendFactory INSTANCE = new DRedReteBackendFactory();
24
25 @Override
26 public IQueryBackend create(IQueryBackendContext context) {
27 return create(context, true, null);
28 }
29
30 @Override
31 public int hashCode() {
32 return DRedReteBackendFactory.class.hashCode();
33 }
34
35 @Override
36 public boolean equals(final Object obj) {
37 if (this == obj) {
38 return true;
39 }
40 if (obj == null) {
41 return false;
42 }
43 if (!(obj instanceof DRedReteBackendFactory)) {
44 return false;
45 }
46 return true;
47 }
48
49}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.matcher;
10
11import java.util.HashMap;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
15import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
16import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
17
18/**
19 * A configurable hint provider that gathers hints for queries during runtime, and delegates defaults to an external hint provider.
20 *
21 * @author Gabor Bergmann
22 * @since 1.5
23 */
24class HintConfigurator implements IQueryBackendHintProvider {
25
26 private IQueryBackendHintProvider defaultHintProvider;
27 private Map<PQuery, QueryEvaluationHint> storedHints = new HashMap<PQuery, QueryEvaluationHint>();
28
29 public HintConfigurator(IQueryBackendHintProvider defaultHintProvider) {
30 this.defaultHintProvider = defaultHintProvider;
31 }
32
33 @Override
34 public QueryEvaluationHint getQueryEvaluationHint(PQuery query) {
35 return defaultHintProvider.getQueryEvaluationHint(query).overrideBy(storedHints.get(query));
36 }
37
38 public void storeHint(PQuery query, QueryEvaluationHint hint) {
39 QueryEvaluationHint oldHint = storedHints.get(query);
40 if (oldHint == null)
41 storedHints.put(query, hint);
42 else
43 storedHints.put(query, oldHint.overrideBy(hint));
44 }
45
46}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.matcher;
10
11import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
12
13/**
14 * @author Grill Balázs
15 * @since 1.4
16 *
17 */
18public class IncrementalMatcherCapability implements IMatcherCapability {
19
20 @Override
21 public boolean canBeSubstitute(IMatcherCapability capability) {
22 /*
23 * TODO: for now, as we are only prepared for Rete and LS, we can assume that
24 * a matcher created with Rete can always be a substitute for a matcher created
25 * by any backend.
26 */
27 return true;
28 }
29
30}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.matcher;
10
11import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability;
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
13import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
14import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
15import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
16import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
17import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
18import tools.refinery.viatra.runtime.rete.construction.plancompiler.ReteRecipeCompiler;
19import tools.refinery.viatra.runtime.rete.util.Options;
20
21public class ReteBackendFactory implements IQueryBackendFactory {
22 /**
23 * EXPERIMENTAL
24 */
25 protected static final int reteThreads = 0;
26
27 /**
28 * @since 2.0
29 */
30 public static final ReteBackendFactory INSTANCE = new ReteBackendFactory();
31
32 /**
33 * @deprecated Use the static {@link #INSTANCE} field instead
34 */
35 @Deprecated
36 public ReteBackendFactory() {
37 }
38
39 /**
40 * @since 1.5
41 */
42 @Override
43 public IQueryBackend create(IQueryBackendContext context) {
44 return create(context, false, null);
45 }
46
47 /**
48 * @since 2.4
49 */
50 public IQueryBackend create(IQueryBackendContext context, boolean deleteAndRederiveEvaluation,
51 TimelyConfiguration timelyConfiguration) {
52 ReteEngine engine;
53 engine = new ReteEngine(context, reteThreads, deleteAndRederiveEvaluation, timelyConfiguration);
54 IQueryBackendHintProvider hintConfiguration = engine.getHintConfiguration();
55 ReteRecipeCompiler compiler = new ReteRecipeCompiler(
56 Options.builderMethod.layoutStrategy(context, hintConfiguration), context.getLogger(),
57 context.getRuntimeContext().getMetaContext(), context.getQueryCacheContext(), hintConfiguration,
58 context.getQueryAnalyzer(), deleteAndRederiveEvaluation, timelyConfiguration);
59 engine.setCompiler(compiler);
60 return engine;
61 }
62
63 @Override
64 public Class<? extends IQueryBackend> getBackendClass() {
65 return ReteEngine.class;
66 }
67
68 @Override
69 public int hashCode() {
70 return ReteBackendFactory.class.hashCode();
71 }
72
73 @Override
74 public boolean equals(Object obj) {
75 if (this == obj) {
76 return true;
77 }
78 if (obj == null) {
79 return false;
80 }
81 if (!(obj instanceof ReteBackendFactory)) {
82 return false;
83 }
84 return true;
85 }
86
87 /**
88 * @since 1.4
89 */
90 @Override
91 public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint) {
92 return new IncrementalMatcherCapability();
93 }
94
95 @Override
96 public boolean isCaching() {
97 return true;
98 }
99
100} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.matcher;
10
11import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactoryProvider;
13
14/**
15 * @since 2.0
16 *
17 */
18public class ReteBackendFactoryProvider implements IQueryBackendFactoryProvider {
19
20 @Override
21 public IQueryBackendFactory getFactory() {
22 return ReteBackendFactory.INSTANCE;
23 }
24
25 @Override
26 public boolean isSystemDefaultEngine() {
27 return true;
28 }
29
30 @Override
31 public boolean isSystemDefaultCachingBackend() {
32 return true;
33 }
34
35}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.matcher;
11
12import java.lang.reflect.InvocationTargetException;
13import java.util.Collection;
14import java.util.LinkedList;
15import java.util.Map;
16import java.util.concurrent.Callable;
17
18import org.apache.log4j.Logger;
19import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException;
20import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
21import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory;
22import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
23import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
24import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
25import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
26import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
27import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
28import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
29import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
30import tools.refinery.viatra.runtime.rete.boundary.Disconnectable;
31import tools.refinery.viatra.runtime.rete.boundary.ReteBoundary;
32import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException;
33import tools.refinery.viatra.runtime.rete.construction.plancompiler.ReteRecipeCompiler;
34import tools.refinery.viatra.runtime.rete.index.Indexer;
35import tools.refinery.viatra.runtime.rete.network.Network;
36import tools.refinery.viatra.runtime.rete.network.NodeProvisioner;
37import tools.refinery.viatra.runtime.rete.network.ReteContainer;
38import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
39
40/**
41 * @author Gabor Bergmann
42 *
43 */
44public class ReteEngine implements IQueryBackend {
45
46 protected Network reteNet;
47 protected final int reteThreads;
48 protected ReteBoundary boundary;
49
50 /**
51 * @since 2.2
52 */
53 protected final boolean deleteAndRederiveEvaluation;
54 /**
55 * @since 2.4
56 */
57 protected final TimelyConfiguration timelyConfiguration;
58
59 private IQueryBackendContext context;
60 private Logger logger;
61 protected IQueryRuntimeContext runtimeContext;
62
63 protected Collection<Disconnectable> disconnectables;
64
65 protected Map<PQuery, RetePatternMatcher> matchers;
66
67 protected ReteRecipeCompiler compiler;
68
69 protected final boolean parallelExecutionEnabled; // TRUE if model manipulation can go on
70
71 private boolean disposedOrUninitialized = true;
72
73 private HintConfigurator hintConfigurator;
74
75 /**
76 * @param context
77 * the context of the pattern matcher, conveying all information from the outside world.
78 * @param reteThreads
79 * the number of threads to operate the RETE network with; 0 means single-threaded operation, 1 starts an
80 * asynchronous thread to operate the RETE net, >1 uses multiple RETE containers.
81 */
82 public ReteEngine(IQueryBackendContext context, int reteThreads) {
83 this(context, reteThreads, false, null);
84 }
85
86 /**
87 * @since 2.4
88 */
89 public ReteEngine(IQueryBackendContext context, int reteThreads, boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyConfiguration) {
90 super();
91 this.context = context;
92 this.logger = context.getLogger();
93 this.runtimeContext = context.getRuntimeContext();
94 this.reteThreads = reteThreads;
95 this.parallelExecutionEnabled = reteThreads > 0;
96 this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation;
97 this.timelyConfiguration = timelyConfiguration;
98 initEngine();
99 this.compiler = null;
100 }
101
102 /**
103 * @since 1.6
104 */
105 public IQueryBackendContext getBackendContext() {
106 return context;
107 }
108
109 /**
110 * @since 2.2
111 */
112 public boolean isDeleteAndRederiveEvaluation() {
113 return this.deleteAndRederiveEvaluation;
114 }
115
116 /**
117 * @since 2.4
118 */
119 public TimelyConfiguration getTimelyConfiguration() {
120 return this.timelyConfiguration;
121 }
122
123 /**
124 * initializes engine components
125 */
126 private synchronized void initEngine() {
127 this.disposedOrUninitialized = false;
128 this.disconnectables = new LinkedList<Disconnectable>();
129 // this.caughtExceptions = new LinkedBlockingQueue<Throwable>();
130
131
132 this.hintConfigurator = new HintConfigurator(context.getHintProvider());
133
134 this.reteNet = new Network(reteThreads, this);
135 this.boundary = new ReteBoundary(this); // prerequisite: network
136
137 this.matchers = CollectionsFactory.createMap();
138 /* this.matchersScoped = new HashMap<PatternDescription, Map<Map<Integer,Scope>,RetePatternMatcher>>(); */
139
140 // prerequisite: network, framework, boundary, disconnectables
141 //context.subscribeBackendForUpdates(this.boundary);
142 // prerequisite: boundary, disconnectables
143// this.traceListener = context.subscribePatternMatcherForTraceInfluences(this);
144
145 }
146
147 @Override
148 public void flushUpdates() {
149 for (ReteContainer container : this.reteNet.getContainers()) {
150 container.deliverMessagesSingleThreaded();
151 }
152 }
153
154 /**
155 * deconstructs engine components
156 */
157 private synchronized void deconstructEngine() {
158 ensureInitialized();
159 reteNet.kill();
160
161 //context.unSubscribeBackendFromUpdates(this.boundary);
162 for (Disconnectable disc : disconnectables) {
163 disc.disconnect();
164 }
165
166 this.matchers = null;
167 this.disconnectables = null;
168
169 this.reteNet = null;
170 this.boundary = null;
171
172 this.hintConfigurator = null;
173
174 // this.machineListener = new MachineListener(this); // prerequisite:
175 // framework, disconnectables
176// this.traceListener = null;
177
178 this.disposedOrUninitialized = true;
179 }
180
181 /**
182 * Deconstructs the engine to get rid of it finally
183 */
184 public void killEngine() {
185 deconstructEngine();
186 // this.framework = null;
187 this.compiler = null;
188 this.logger = null;
189 }
190
191 /**
192 * Resets the engine to an after-initialization phase
193 *
194 */
195 public void reset() {
196 deconstructEngine();
197
198 initEngine();
199
200 compiler.reset();
201 }
202
203 /**
204 * Accesses the patternmatcher for a given pattern, constructs one if a matcher is not available yet.
205 *
206 * @pre: builder is set.
207 * @param query
208 * the pattern to be matched.
209 * @return a patternmatcher object that can match occurences of the given pattern.
210 * @throws ViatraQueryRuntimeException
211 * if construction fails.
212 */
213 public synchronized RetePatternMatcher accessMatcher(final PQuery query) {
214 ensureInitialized();
215 RetePatternMatcher matcher;
216 // String namespace = gtPattern.getNamespace().getName();
217 // String name = gtPattern.getName();
218 // String fqn = namespace + "." + name;
219 matcher = matchers.get(query);
220 if (matcher == null) {
221 constructionWrapper(() -> {
222 RecipeTraceInfo prodNode;
223 prodNode = boundary.accessProductionTrace(query);
224
225 RetePatternMatcher retePatternMatcher = new RetePatternMatcher(ReteEngine.this,
226 prodNode);
227 retePatternMatcher.setTag(query);
228 matchers.put(query, retePatternMatcher);
229 return null;
230 });
231 matcher = matchers.get(query);
232 }
233
234 executeDelayedCommands();
235
236 return matcher;
237 }
238
239
240 /**
241 * Constructs RETE pattern matchers for a collection of patterns, if they are not available yet. Model traversal
242 * during the whole construction period is coalesced (which may have an effect on performance, depending on the
243 * matcher context).
244 *
245 * @pre: builder is set.
246 * @param specifications
247 * the patterns to be matched.
248 * @throws ViatraQueryRuntimeException
249 * if construction fails.
250 */
251 public synchronized void buildMatchersCoalesced(final Collection<PQuery> specifications) {
252 ensureInitialized();
253 constructionWrapper(() -> {
254 for (PQuery specification : specifications) {
255 boundary.accessProductionNode(specification);
256 }
257 return null;
258 });
259 }
260
261 /**
262 * @since 2.4
263 */
264 public <T> T constructionWrapper(final Callable<T> payload) {
265 T result = null;
266// context.modelReadLock();
267// try {
268 if (parallelExecutionEnabled)
269 reteNet.getStructuralChangeLock().lock();
270 try {
271 try {
272 result = runtimeContext.coalesceTraversals(() -> {
273 T innerResult = payload.call();
274 this.executeDelayedCommands();
275 return innerResult;
276 });
277 } catch (InvocationTargetException ex) {
278 final Throwable cause = ex.getCause();
279 if (cause instanceof RetePatternBuildException)
280 throw (RetePatternBuildException) cause;
281 if (cause instanceof RuntimeException)
282 throw (RuntimeException) cause;
283 assert (false);
284 }
285 } finally {
286 if (parallelExecutionEnabled)
287 reteNet.getStructuralChangeLock().unlock();
288 reteNet.waitForReteTermination();
289 }
290// } finally {
291// context.modelReadUnLock();
292// }
293 return result;
294 }
295
296 // /**
297 // * Accesses the patternmatcher for a given pattern with additional scoping, constructs one if
298 // * a matcher is not available yet.
299 // *
300 // * @param gtPattern
301 // * the pattern to be matched.
302 // * @param additionalScopeMap
303 // * additional, optional scopes for the symbolic parameters
304 // * maps the position of the symbolic parameter to its additional scope (if any)
305 // * @pre: scope.parent is non-root, i.e. this is a nontrivial constraint
306 // * use the static method RetePatternMatcher.buildAdditionalScopeMap() to create from PatternCallSignature
307 // * @return a patternmatcher object that can match occurences of the given
308 // * pattern.
309 // * @throws PatternMatcherCompileTimeException
310 // * if construction fails.
311 // */
312 // public synchronized RetePatternMatcher accessMatcherScoped(PatternDescription gtPattern, Map<Integer, Scope>
313 // additionalScopeMap)
314 // throws PatternMatcherCompileTimeException {
315 // if (additionalScopeMap.isEmpty()) return accessMatcher(gtPattern);
316 //
317 // RetePatternMatcher matcher;
318 //
319 // Map<Map<Integer, Scope>, RetePatternMatcher> scopes = matchersScoped.get(gtPattern);
320 // if (scopes == null) {
321 // scopes = new HashMap<Map<Integer, Scope>, RetePatternMatcher>();
322 // matchersScoped.put(gtPattern, scopes);
323 // }
324 //
325 // matcher = scopes.get(additionalScopeMap);
326 // if (matcher == null) {
327 // context.modelReadLock();
328 // try {
329 // reteNet.getStructuralChangeLock().lock();
330 // try {
331 // Address<? extends Production> prodNode;
332 // prodNode = boundary.accessProductionScoped(gtPattern, additionalScopeMap);
333 //
334 // matcher = new RetePatternMatcher(this, prodNode);
335 // scopes.put(additionalScopeMap, matcher);
336 // } finally {
337 // reteNet.getStructuralChangeLock().unlock();
338 // }
339 // } finally {
340 // context.modelReadUnLock();
341 // }
342 // // reteNet.flushUpdates();
343 // }
344 //
345 // return matcher;
346 // }
347
348 /**
349 * Returns an indexer that groups the contents of this Production node by their projections to a given mask.
350 * Designed to be called by a RetePatternMatcher.
351 *
352 * @param production
353 * the production node to be indexed.
354 * @param mask
355 * the mask that defines the projection.
356 * @return the Indexer.
357 */
358 synchronized Indexer accessProjection(RecipeTraceInfo production, TupleMask mask) {
359 ensureInitialized();
360 NodeProvisioner nodeProvisioner = reteNet.getHeadContainer().getProvisioner();
361 Indexer result = nodeProvisioner.peekProjectionIndexer(production, mask);
362 if (result == null) {
363 result = constructionWrapper(() ->
364 nodeProvisioner.accessProjectionIndexerOnetime(production, mask)
365 );
366 }
367
368 return result;
369 }
370
371 // /**
372 // * Retrieves the patternmatcher for a given pattern fqn, returns null if
373 // the matching network hasn't been constructed yet.
374 // *
375 // * @param fqn the fully qualified name of the pattern to be matched.
376 // * @return the previously constructed patternmatcher object that can match
377 // occurences of the given pattern, or null if it doesn't exist.
378 // */
379 // public RetePatternMatcher getMatcher(String fqn)
380 // {
381 // RetePatternMatcher matcher = matchersByFqn.get(fqn);
382 // if (matcher == null)
383 // {
384 // Production prodNode = boundary.getProduction(fqn);
385 //
386 // matcher = new RetePatternMatcher(this, prodNode);
387 // matchersByFqn.put(fqn, matcher);
388 // }
389 //
390 // return matcher;
391 // }
392
393 /**
394 * @since 2.3
395 */
396 public void executeDelayedCommands() {
397 for (final ReteContainer container : this.reteNet.getContainers()) {
398 container.executeDelayedCommands();
399 }
400 }
401
402 /**
403 * Waits until the pattern matcher is in a steady state and output can be retrieved.
404 */
405 public void settle() {
406 ensureInitialized();
407 reteNet.waitForReteTermination();
408 }
409
410 /**
411 * Waits until the pattern matcher is in a steady state and output can be retrieved. When steady state is reached, a
412 * retrieval action is executed before the steady state ceases.
413 *
414 * @param action
415 * the action to be run when reaching the steady-state.
416 */
417 public void settle(Runnable action) {
418 ensureInitialized();
419 reteNet.waitForReteTermination(action);
420 }
421
422 // /**
423 // * @return the framework
424 // */
425 // public IFramework getFramework() {
426 // return framework.get();
427 // }
428
429 /**
430 * @return the reteNet
431 */
432 public Network getReteNet() {
433 ensureInitialized();
434 return reteNet;
435 }
436
437 /**
438 * @return the boundary
439 */
440 public ReteBoundary getBoundary() {
441 ensureInitialized();
442 return boundary;
443 }
444
445 // /**
446 // * @return the pattern matcher builder
447 // */
448 // public IRetePatternBuilder getBuilder() {
449 // return builder;
450 // }
451
452 /**
453 * @param builder
454 * the pattern matcher builder to set
455 */
456 public void setCompiler(ReteRecipeCompiler builder) {
457 ensureInitialized();
458 this.compiler = builder;
459 }
460
461// /**
462// * @return the manipulationListener
463// */
464// public IManipulationListener getManipulationListener() {
465// ensureInitialized();
466// return manipulationListener;
467// }
468
469// /**
470// * @return the traceListener
471// */
472// public IPredicateTraceListener geTraceListener() {
473// ensureInitialized();
474// return traceListener;
475// }
476
477 /**
478 * @param disc
479 * the new Disconnectable adapter.
480 */
481 public void addDisconnectable(Disconnectable disc) {
482 ensureInitialized();
483 disconnectables.add(disc);
484 }
485
486 /**
487 * @return the parallelExecutionEnabled
488 */
489 public boolean isParallelExecutionEnabled() {
490 return parallelExecutionEnabled;
491 }
492
493
494 public Logger getLogger() {
495 ensureInitialized();
496 return logger;
497 }
498
499 public IQueryRuntimeContext getRuntimeContext() {
500 ensureInitialized();
501 return runtimeContext;
502 }
503
504 public ReteRecipeCompiler getCompiler() {
505 ensureInitialized();
506 return compiler;
507 }
508
509 // /**
510 // * For internal use only: logs exceptions occurring during term evaluation inside the RETE net.
511 // * @param e
512 // */
513 // public void logEvaluatorException(Throwable e) {
514 // try {
515 // caughtExceptions.put(e);
516 // } catch (InterruptedException e1) {
517 // logEvaluatorException(e);
518 // }
519 // }
520 // /**
521 // * Polls the exceptions caught and logged during term evaluation by this RETE engine.
522 // * Recommended usage: iterate polling until null is returned.
523 // *
524 // * @return the next caught exception, or null if there are no more.
525 // */
526 // public Throwable getNextLoggedEvaluatorException() {
527 // return caughtExceptions.poll();
528 // }
529
530 void ensureInitialized() {
531 if (disposedOrUninitialized)
532 throw new IllegalStateException("Trying to use a Rete engine that has been disposed or has not yet been initialized.");
533
534 }
535
536 @Override
537 public IQueryResultProvider getResultProvider(PQuery query) {
538 return accessMatcher(query);
539 }
540
541 /**
542 * @since 1.4
543 */
544 @Override
545 public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints) {
546 hintConfigurator.storeHint(query, hints);
547 return accessMatcher(query);
548 }
549
550 @Override
551 public IQueryResultProvider peekExistingResultProvider(PQuery query) {
552 ensureInitialized();
553 return matchers.get(query);
554 }
555
556 @Override
557 public void dispose() {
558 killEngine();
559 }
560
561 @Override
562 public boolean isCaching() {
563 return true;
564 }
565
566 /**
567 * @since 1.5
568 * @noreference Internal API, subject to change
569 */
570 public IQueryBackendHintProvider getHintConfiguration() {
571 return hintConfigurator;
572 }
573
574 @Override
575 public IQueryBackendFactory getFactory() {
576 return ReteBackendFactory.INSTANCE;
577 }
578
579}
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..38fe7c2f
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java
@@ -0,0 +1,463 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10
11package tools.refinery.viatra.runtime.rete.matcher;
12
13import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
14import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider;
15import tools.refinery.viatra.runtime.matchers.backend.IUpdateable;
16import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
17import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
21import tools.refinery.viatra.runtime.matchers.util.Accuracy;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
23import tools.refinery.viatra.runtime.rete.index.Indexer;
24import tools.refinery.viatra.runtime.rete.index.IterableIndexer;
25import tools.refinery.viatra.runtime.rete.network.Node;
26import tools.refinery.viatra.runtime.rete.network.ProductionNode;
27import tools.refinery.viatra.runtime.rete.network.Receiver;
28import tools.refinery.viatra.runtime.rete.remote.Address;
29import tools.refinery.viatra.runtime.rete.single.CallbackNode;
30import tools.refinery.viatra.runtime.rete.single.TransformerNode;
31import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
32
33import java.util.Collection;
34import java.util.List;
35import java.util.Map;
36import java.util.Optional;
37import java.util.stream.Collectors;
38import java.util.stream.Stream;
39
40/**
41 * @author Gabor Bergmann
42 *
43 */
44public class RetePatternMatcher extends TransformerNode implements IQueryResultProvider {
45
46 protected ReteEngine engine;
47 protected IQueryRuntimeContext context;
48 protected ProductionNode productionNode;
49 protected RecipeTraceInfo productionNodeTrace;
50 protected Map<String, Integer> posMapping;
51 protected Map<Object, Receiver> taggedChildren = CollectionsFactory.createMap();
52 protected boolean connected = false; // is rete-wise connected to the
53 // production node?
54
55 /**
56 * @param productionNode
57 * a production node that matches this pattern without any parameter bindings
58 * @pre: Production must be local to the head container
59 */
60 public RetePatternMatcher(ReteEngine engine, RecipeTraceInfo productionNodeTrace) {
61 super(engine.getReteNet().getHeadContainer());
62 this.engine = engine;
63 this.context = engine.getRuntimeContext();
64 this.productionNodeTrace = productionNodeTrace;
65 final Address<? extends Node> productionAddress = reteContainer.getProvisioner()
66 .getOrCreateNodeByRecipe(productionNodeTrace);
67 if (!reteContainer.isLocal(productionAddress))
68 throw new IllegalArgumentException("@pre: Production must be local to the head container");
69 this.productionNode = (ProductionNode) reteContainer.resolveLocal(productionAddress);
70 this.posMapping = this.productionNode.getPosMapping();
71 this.reteContainer.getCommunicationTracker().registerDependency(this.productionNode, this);
72 }
73
74 /**
75 * @since 1.6
76 */
77 public ProductionNode getProductionNode() {
78 return productionNode;
79 }
80
81 public Tuple matchOneRandomly(Object[] inputMapping, boolean[] fixed) {
82 List<Tuple> allMatches = matchAll(inputMapping, fixed).collect(Collectors.toList());
83 if (allMatches == null || allMatches.isEmpty())
84 return null;
85 else
86 return allMatches.get((int) (Math.random() * allMatches.size()));
87 }
88
89 /**
90 * @since 2.0
91 */
92 public Stream<Tuple> matchAll(Object[] inputMapping, boolean[] fixed) {
93 // retrieving the projection
94 TupleMask mask = TupleMask.fromKeepIndicators(fixed);
95 Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping));
96
97 return matchAll(mask, inputSignature);
98
99 }
100
101 /**
102 * @since 2.0
103 */
104 public Stream<Tuple> matchAll(TupleMask mask, ITuple inputSignature) {
105 AllMatchFetcher fetcher = new AllMatchFetcher(engine.accessProjection(productionNodeTrace, mask),
106 context.wrapTuple(inputSignature.toImmutable()));
107 engine.reteNet.waitForReteTermination(fetcher);
108 return fetcher.getMatches();
109
110 }
111
112 /**
113 * @since 2.0
114 */
115 public Optional<Tuple> matchOne(Object[] inputMapping, boolean[] fixed) {
116 // retrieving the projection
117 TupleMask mask = TupleMask.fromKeepIndicators(fixed);
118 Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping));
119
120 return matchOne(mask, inputSignature);
121 }
122
123 /**
124 * @since 2.0
125 */
126 public Optional<Tuple> matchOne(TupleMask mask, ITuple inputSignature) {
127 SingleMatchFetcher fetcher = new SingleMatchFetcher(engine.accessProjection(productionNodeTrace, mask),
128 context.wrapTuple(inputSignature.toImmutable()));
129 engine.reteNet.waitForReteTermination(fetcher);
130 return Optional.ofNullable(fetcher.getMatch());
131 }
132
133 /**
134 * Counts the number of occurrences of the pattern that match inputMapping on positions where fixed is true.
135 *
136 * @return the number of occurrences
137 */
138 public int count(Object[] inputMapping, boolean[] fixed) {
139 TupleMask mask = TupleMask.fromKeepIndicators(fixed);
140 Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping));
141
142 return count(mask, inputSignature);
143 }
144
145 /**
146 * Counts the number of occurrences of the pattern that match inputMapping on positions where fixed is true.
147 *
148 * @return the number of occurrences
149 * @since 1.7
150 */
151 public int count(TupleMask mask, ITuple inputSignature) {
152 CountFetcher fetcher = new CountFetcher(engine.accessProjection(productionNodeTrace, mask),
153 context.wrapTuple(inputSignature.toImmutable()));
154 engine.reteNet.waitForReteTermination(fetcher);
155
156 return fetcher.getCount();
157 }
158
159 /**
160 * Counts the number of distinct tuples attainable from the match set by projecting match tuples according to the given mask.
161 *
162 *
163 * @return the size of the projection
164 * @since 2.1
165 */
166 public int projectionSize(TupleMask groupMask) {
167 ProjectionSizeFetcher fetcher = new ProjectionSizeFetcher(
168 (IterableIndexer) engine.accessProjection(productionNodeTrace, groupMask));
169 engine.reteNet.waitForReteTermination(fetcher);
170
171 return fetcher.getSize();
172 }
173
174 /**
175 * Connects a new external receiver that will receive update notifications from now on. The receiver will
176 * practically connect to the production node, the added value is unwrapping the updates for external use.
177 *
178 * @param synchronize
179 * if true, the contents of the production node will be inserted into the receiver after the connection
180 * is established.
181 */
182 public synchronized void connect(Receiver receiver, boolean synchronize) {
183 if (!connected) { // connect to the production node as a RETE-child
184 reteContainer.connect(productionNode, this);
185 connected = true;
186 }
187 if (synchronize)
188 reteContainer.connectAndSynchronize(this, receiver);
189 else
190 reteContainer.connect(this, receiver);
191 }
192
193 /**
194 * Connects a new external receiver that will receive update notifications from now on. The receiver will
195 * practically connect to the production node, the added value is unwrapping the updates for external use.
196 *
197 * The external receiver will be disconnectable later based on its tag.
198 *
199 * @param tag
200 * an identifier to recognize the child node by.
201 *
202 * @param synchronize
203 * if true, the contents of the production node will be inserted into the receiver after the connection
204 * is established.
205 *
206 */
207 public synchronized void connect(Receiver receiver, Object tag, boolean synchronize) {
208 taggedChildren.put(tag, receiver);
209 connect(receiver, synchronize);
210 }
211
212 /**
213 * Disconnects a child node.
214 */
215 public synchronized void disconnect(Receiver receiver) {
216 reteContainer.disconnect(this, receiver);
217 }
218
219 /**
220 * Disconnects the child node that was connected by specifying the given tag.
221 *
222 * @return if a child node was found registered with this tag.
223 */
224 public synchronized boolean disconnectByTag(Object tag) {
225 final Receiver receiver = taggedChildren.remove(tag);
226 final boolean found = receiver != null;
227 if (found)
228 disconnect(receiver);
229 return found;
230 }
231
232 @Override
233 protected Tuple transform(Tuple input) {
234 return context.unwrapTuple(input);
235 }
236
237 abstract class AbstractMatchFetcher implements Runnable {
238 Indexer indexer;
239 Tuple signature;
240
241 public AbstractMatchFetcher(Indexer indexer, Tuple signature) {
242 super();
243 this.indexer = indexer;
244 this.signature = signature;
245 }
246
247 @Override
248 public void run() {
249 fetch(indexer.get(signature));
250 }
251
252 protected abstract void fetch(Collection<Tuple> matches);
253
254 }
255
256 class AllMatchFetcher extends AbstractMatchFetcher {
257
258 public AllMatchFetcher(Indexer indexer, Tuple signature) {
259 super(indexer, signature);
260 }
261
262 Stream<Tuple> matches = null;
263
264 public Stream<Tuple> getMatches() {
265 return matches;
266 }
267
268 @Override
269 protected void fetch(Collection<Tuple> matches) {
270 if (matches == null)
271 this.matches = Stream.of();
272 else {
273 this.matches = matches.stream().map(context::unwrapTuple);
274 }
275
276 }
277
278 }
279
280 class SingleMatchFetcher extends AbstractMatchFetcher {
281
282 public SingleMatchFetcher(Indexer indexer, Tuple signature) {
283 super(indexer, signature);
284 }
285
286 Tuple match = null;
287
288 public Tuple getMatch() {
289 return match;
290 }
291
292 @Override
293 protected void fetch(Collection<Tuple> matches) {
294 if (matches != null && !matches.isEmpty())
295 match = context.unwrapTuple(matches.iterator().next());
296 }
297
298 // public void run() {
299 // Collection<Tuple> unscopedMatches = indexer.get(signature);
300 //
301 // // checking scopes
302 // if (unscopedMatches != null) {
303 // for (Tuple um : /* productionNode */unscopedMatches) {
304 // match = inputConnector.unwrapTuple(um);
305 // return;
306 //
307 // // Tuple ps = inputConnector.unwrapTuple(um);
308 // // boolean ok = true;
309 // // if (!ignoreScope) for (int k = 0; (k < ps.getSize()) && ok; k++) {
310 // // if (pcs[k].getParameterMode() == ParameterMode.INPUT) {
311 // // // ok = ok && (inputMapping[k]==ps.elements[k]);
312 // // // should now be true
313 // // } else // ParameterMode.OUTPUT
314 // // {
315 // // IEntity scopeParent = (IEntity) pcs[k].getParameterScope().getParent();
316 // // Integer containmentMode = pcs[k].getParameterScope().getContainmentMode();
317 // // if (containmentMode == Scope.BELOW)
318 // // ok = ok && ((IModelElement) ps.get(k)).isBelowNamespace(scopeParent);
319 // // else
320 // // /* case Scope.IN: */
321 // // ok = ok && scopeParent.equals(((IModelElement) ps.get(k)).getNamespace());
322 // // // note: getNamespace returns null instead of the
323 // // // (imaginary) modelspace root entity for top level
324 // // // elements;
325 // // // this is not a problem here as Scope.IN implies
326 // // // scopeParent != root.
327 // //
328 // // }
329 // // }
330 // //
331 // // if (ok) {
332 // // reteMatching = new ReteMatching(ps, posMapping);
333 // // return;
334 // // }
335 // }
336 // }
337 //
338 // }
339
340 }
341
342 class CountFetcher extends AbstractMatchFetcher {
343
344 public CountFetcher(Indexer indexer, Tuple signature) {
345 super(indexer, signature);
346 }
347
348 int count = 0;
349
350 public int getCount() {
351 return count;
352 }
353
354 @Override
355 protected void fetch(Collection<Tuple> matches) {
356 count = matches == null ? 0 : matches.size();
357 }
358
359 }
360
361 class ProjectionSizeFetcher implements Runnable {
362 IterableIndexer indexer;
363 int size = 0;
364
365 public ProjectionSizeFetcher(IterableIndexer indexer) {
366 super();
367 this.indexer = indexer;
368 }
369
370 @Override
371 public void run() {
372 size = indexer.getBucketCount();
373 }
374
375 public int getSize() {
376 return size;
377 }
378
379 }
380
381 private boolean[] notNull(Object[] parameters) {
382 boolean[] notNull = new boolean[parameters.length];
383 for (int i = 0; i < parameters.length; ++i)
384 notNull[i] = parameters[i] != null;
385 return notNull;
386 }
387
388
389
390 @Override
391 public boolean hasMatch(Object[] parameters) {
392 return countMatches(parameters) > 0;
393 }
394
395 @Override
396 public boolean hasMatch(TupleMask parameterSeedMask, ITuple parameters) {
397 return count(parameterSeedMask, parameters) > 0;
398 }
399
400 @Override
401 public int countMatches(Object[] parameters) {
402 return count(parameters, notNull(parameters));
403 }
404
405 @Override
406 public int countMatches(TupleMask parameterSeedMask, ITuple parameters) {
407 return count(parameterSeedMask, parameters);
408 }
409
410
411 @Override
412 public Optional<Long> estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy) {
413 return Optional.of((long)projectionSize(groupMask)); // always accurate
414 }
415
416 @Override
417 public Optional<Tuple> getOneArbitraryMatch(Object[] parameters) {
418 return matchOne(parameters, notNull(parameters));
419 }
420
421 @Override
422 public Optional<Tuple> getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters) {
423 return matchOne(parameterSeedMask, parameters);
424 }
425
426 @Override
427 public Stream<Tuple> getAllMatches(Object[] parameters) {
428 return matchAll(parameters, notNull(parameters));
429 }
430
431 @Override
432 public Stream<Tuple> getAllMatches(TupleMask parameterSeedMask, ITuple parameters) {
433 return matchAll(parameterSeedMask, parameters);
434 }
435
436 @Override
437 public IQueryBackend getQueryBackend() {
438 return engine;
439 }
440
441 @Override
442 public void addUpdateListener(final IUpdateable listener, final Object listenerTag, boolean fireNow) {
443 // As a listener is added as a delayed command, they should be executed to make sure everything is consistent on
444 // return, see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=562369
445 engine.constructionWrapper(() -> {
446 final CallbackNode callbackNode = new CallbackNode(this.reteContainer, listener);
447 connect(callbackNode, listenerTag, fireNow);
448 return null;
449 });
450 }
451
452 @Override
453 public void removeUpdateListener(Object listenerTag) {
454 engine.constructionWrapper(() -> {
455 disconnectByTag(listenerTag);
456 return null;
457 });
458 }
459
460 public Indexer getInternalIndexer(TupleMask mask) {
461 return engine.accessProjection(productionNodeTrace, mask);
462 }
463}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.matcher;
10
11/**
12 * Configuration of timely evaluation.
13 *
14 * @author Tamas Szabo
15 * @since 2.4
16 */
17public class TimelyConfiguration {
18
19 private final AggregatorArchitecture aggregatorArchitecture;
20 private final TimelineRepresentation timelineRepresentation;
21
22 public TimelyConfiguration(final TimelineRepresentation timelineRepresentation,
23 final AggregatorArchitecture aggregatorArchitecture) {
24 this.aggregatorArchitecture = aggregatorArchitecture;
25 this.timelineRepresentation = timelineRepresentation;
26 }
27
28 public AggregatorArchitecture getAggregatorArchitecture() {
29 return aggregatorArchitecture;
30 }
31
32 public TimelineRepresentation getTimelineRepresentation() {
33 return timelineRepresentation;
34 }
35
36 public enum AggregatorArchitecture {
37 /**
38 * Aggregands are copied over from lower timestamps to higher timestamps.
39 */
40 PARALLEL,
41
42 /**
43 * Aggregands are only present at the timestamp where they are inserted at.
44 * Only aggregate results are pushed towards higher timestamps during folding.
45 */
46 SEQUENTIAL
47 }
48
49 public enum TimelineRepresentation {
50 /**
51 * Only the first moment (timestamp) of appearance is maintained per tuple.
52 */
53 FIRST_ONLY,
54
55 /**
56 * Complete timeline (series of appearance & disappearance) is maintained per tuple.
57 */
58 FAITHFUL
59 }
60
61}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.matcher;
10
11import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend;
12import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
13import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.AggregatorArchitecture;
14import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
15
16/**
17 * A {@link ReteBackendFactory} implementation that creates {@link ReteEngine}s that use non-scattered timely
18 * evaluation.
19 *
20 * @author Tamas Szabo
21 * @since 2.4
22 */
23public class TimelyReteBackendFactory extends ReteBackendFactory {
24
25 private final TimelyConfiguration configuration;
26
27 public static final TimelyReteBackendFactory FIRST_ONLY_SEQUENTIAL = new TimelyReteBackendFactory(
28 new TimelyConfiguration(TimelineRepresentation.FIRST_ONLY, AggregatorArchitecture.SEQUENTIAL));
29 public static final TimelyReteBackendFactory FIRST_ONLY_PARALLEL = new TimelyReteBackendFactory(
30 new TimelyConfiguration(TimelineRepresentation.FIRST_ONLY, AggregatorArchitecture.PARALLEL));
31 public static final TimelyReteBackendFactory FAITHFUL_SEQUENTIAL = new TimelyReteBackendFactory(
32 new TimelyConfiguration(TimelineRepresentation.FAITHFUL, AggregatorArchitecture.SEQUENTIAL));
33 public static final TimelyReteBackendFactory FAITHFUL_PARALLEL = new TimelyReteBackendFactory(
34 new TimelyConfiguration(TimelineRepresentation.FAITHFUL, AggregatorArchitecture.PARALLEL));
35
36 public TimelyReteBackendFactory(final TimelyConfiguration configuration) {
37 this.configuration = configuration;
38 }
39
40 @Override
41 public IQueryBackend create(final IQueryBackendContext context) {
42 return create(context, false, configuration);
43 }
44
45 @Override
46 public int hashCode() {
47 return TimelyReteBackendFactory.class.hashCode();
48 }
49
50 @Override
51 public boolean equals(final Object obj) {
52 if (this == obj) {
53 return true;
54 }
55 if (obj == null) {
56 return false;
57 }
58 if (!(obj instanceof TimelyReteBackendFactory)) {
59 return false;
60 }
61 return true;
62 }
63
64}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.misc;
11
12import java.util.Collection;
13import java.util.LinkedList;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.rete.network.ReteContainer;
18import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
19
20/**
21 * @author Gabor Bergmann
22 *
23 * A bag is a container that tuples can be dumped into. Does NOT propagate updates! Optimized for small contents
24 * size OR positive updates only.
25 */
26public class Bag extends SimpleReceiver {
27
28 public Collection<Tuple> contents;
29
30 public Bag(ReteContainer reteContainer) {
31 super(reteContainer);
32 contents = new LinkedList<Tuple>();
33 }
34
35 @Override
36 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
37 if (direction == Direction.INSERT)
38 contents.add(updateElement);
39 else
40 contents.remove(updateElement);
41 }
42
43}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.misc;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.StandardNode;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22/**
23 * Node that always contains a single constant Tuple
24 *
25 * @author Gabor Bergmann
26 */
27public class ConstantNode extends StandardNode {
28
29 protected Tuple constant;
30
31 /**
32 * @param constant
33 * will be wrapped using {@link IQueryRuntimeContext#wrapTuple(Tuple)}
34 */
35 public ConstantNode(ReteContainer reteContainer, Tuple constant) {
36 super(reteContainer);
37 this.constant = reteContainer.getNetwork().getEngine().getRuntimeContext().wrapTuple(constant);
38 }
39
40 @Override
41 public void pullInto(Collection<Tuple> collector, boolean flush) {
42 collector.add(constant);
43 }
44
45 @Override
46 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
47 collector.put(constant, Timestamp.INSERT_AT_ZERO_TIMELINE);
48 }
49
50}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.misc;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.rete.network.Network;
14import tools.refinery.viatra.runtime.rete.network.ReteContainer;
15
16/**
17 * Default configuration for DeltaMonitor.
18 *
19 * @author Gabor Bergmann
20 *
21 */
22public class DefaultDeltaMonitor extends DeltaMonitor<Tuple> {
23
24 /**
25 * @param reteContainer
26 */
27 public DefaultDeltaMonitor(ReteContainer reteContainer) {
28 super(reteContainer);
29 }
30
31 /**
32 * @param network
33 */
34 public DefaultDeltaMonitor(Network network) {
35 super(network.getHeadContainer());
36 }
37
38 @Override
39 public Tuple statelessConvert(Tuple tuple) {
40 return tuple;
41 }
42
43}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.misc;
11
12import java.util.Collection;
13import java.util.LinkedHashSet;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Clearable;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20
21/**
22 * A monitoring object that connects to the rete network as a receiver to reflect changes since an arbitrary state
23 * acknowledged by the client. Match tuples are represented by a type MatchType.
24 *
25 * <p>
26 * <b>Usage</b>. If a new matching is found, it appears in the matchFoundEvents collection, and disappears when that
27 * particular matching cannot be found anymore. If the event of finding a match has been processed by the client, it can
28 * be removed manually. In this case, when a previously found matching is lost, the Tuple will appear in the
29 * matchLostEvents collection, and disappear upon finding the same matching again. "Matching lost" events can also be
30 * acknowledged by removing a Tuple from the collection. If the matching is found once again, it will return to
31 * matchFoundEvents.
32 *
33 * <p>
34 * <b>Technical notes</b>. Does NOT propagate updates!
35 *
36 * By overriding statelessConvert(), results can be stored to a MatchType. MatchType must provide equals() and
37 * hashCode() reflecting its contents. The default implementation (DefaultDeltaMonitor) uses Tuple as MatchType.
38 *
39 * By overriding statelessFilter(), some tuples can be filtered.
40 *
41 * @author Gabor Bergmann
42 *
43 */
44public abstract class DeltaMonitor<MatchType> extends SimpleReceiver implements Clearable {
45
46 /**
47 * matches that are newly found
48 */
49 public Collection<MatchType> matchFoundEvents;
50 /**
51 * matches that are newly lost
52 */
53 public Collection<MatchType> matchLostEvents;
54
55 /**
56 * @param reteContainer
57 */
58 public DeltaMonitor(ReteContainer reteContainer) {
59 super(reteContainer);
60 matchFoundEvents = new LinkedHashSet<MatchType>();
61 matchLostEvents = new LinkedHashSet<MatchType>();
62 reteContainer.registerClearable(this);
63 }
64
65 // /**
66 // * Build a delta monitor into the head container of the network.
67 // *
68 // * @param network
69 // */
70 // public DeltaMonitor(Network network) {
71 // this(network.getHeadContainer());
72 // }
73
74 /**
75 * Override this method to provide a lightweight, stateless filter on the tuples
76 *
77 * @param tuple
78 * the occurrence that is to be filtered
79 * @return true if this tuple should be monitored, false if ignored
80 */
81 public boolean statelessFilter(Tuple tuple) {
82 return true;
83 }
84
85 public abstract MatchType statelessConvert(Tuple tuple);
86
87 @Override
88 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
89 if (statelessFilter(updateElement)) {
90 MatchType match = statelessConvert(updateElement);
91 if (direction == Direction.INSERT) {
92 if (!matchLostEvents.remove(match)) // either had before but
93 // lost
94 matchFoundEvents.add(match); // or brand-new
95 } else // revoke
96 {
97 if (!matchFoundEvents.remove(match)) // either never found
98 // in the first
99 // place
100 matchLostEvents.add(match); // or newly lost
101 }
102 }
103 }
104
105 @Override
106 public void clear() {
107 matchFoundEvents.clear();
108 matchLostEvents.clear();
109 }
110
111}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.misc;
10
11import java.util.Collection;
12import java.util.Collections;
13
14import tools.refinery.viatra.runtime.rete.network.BaseNode;
15import tools.refinery.viatra.runtime.rete.network.Receiver;
16import tools.refinery.viatra.runtime.rete.network.ReteContainer;
17import tools.refinery.viatra.runtime.rete.network.Supplier;
18import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
19import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
20import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
21import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
22
23/**
24 * @author Bergmann Gabor
25 *
26 */
27public abstract class SimpleReceiver extends BaseNode implements Receiver {
28
29 protected Supplier parent = null;
30 /**
31 * @since 1.6
32 */
33 protected final Mailbox mailbox;
34
35 /**
36 * @param reteContainer
37 */
38 public SimpleReceiver(ReteContainer reteContainer) {
39 super(reteContainer);
40 mailbox = instantiateMailbox();
41 reteContainer.registerClearable(mailbox);
42 }
43
44 /**
45 * Instantiates the {@link Mailbox} of this receiver.
46 * Subclasses may override this method to provide their own mailbox implementation.
47 *
48 * @return the mailbox
49 * @since 2.0
50 */
51 protected Mailbox instantiateMailbox() {
52 if (this.reteContainer.isTimelyEvaluation()) {
53 return new TimelyMailbox(this, this.reteContainer);
54 } else {
55 return new BehaviorChangingMailbox(this, this.reteContainer);
56 }
57 }
58
59 @Override
60 public Mailbox getMailbox() {
61 return this.mailbox;
62 }
63
64 @Override
65 public void appendParent(Supplier supplier) {
66 if (parent == null)
67 parent = supplier;
68 else
69 throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent
70 + ") and cannot connect to additional parent (" + supplier
71 + ") as it is not a Uniqueness Enforcer Node. ");
72 }
73
74 @Override
75 public void removeParent(Supplier supplier) {
76 if (parent == supplier)
77 parent = null;
78 else
79 throw new IllegalArgumentException("Illegal RETE edge removal: the parent of " + this + " is not "
80 + supplier);
81 }
82
83 @Override
84 public Collection<Supplier> getParents() {
85 if (parent == null)
86 return Collections.emptySet();
87 else
88 return Collections.singleton(parent);
89 }
90
91 /**
92 * Disconnects this node from the network. Can be called publicly.
93 *
94 * @pre: child nodes, if any, must already be disconnected.
95 */
96 public void disconnectFromNetwork() {
97 if (parent != null)
98 reteContainer.disconnect(parent, this);
99 }
100
101 @Override
102 public void assignTraceInfo(TraceInfo traceInfo) {
103 super.assignTraceInfo(traceInfo);
104 if (traceInfo.propagateFromStandardNodeToSupplierParent())
105 if (parent != null)
106 parent.acceptPropagatedTraceInfo(traceInfo);
107 }
108
109} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network;
10
11import java.util.Collections;
12import java.util.HashSet;
13import java.util.Set;
14import java.util.TreeSet;
15
16import tools.refinery.viatra.runtime.rete.traceability.PatternTraceInfo;
17import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
18
19/**
20 * Base implementation for a Rete node.
21 *
22 * @author Bergmann Gabor
23 *
24 */
25public abstract class BaseNode implements Node {
26
27 protected ReteContainer reteContainer;
28 protected long nodeId;
29 protected Object tag;
30 protected Set<TraceInfo> traceInfos;
31
32 /**
33 * @param reteContainer
34 * the container to create this node in
35 */
36 public BaseNode(ReteContainer reteContainer) {
37 super();
38 this.reteContainer = reteContainer;
39 this.nodeId = reteContainer.registerNode(this);
40 this.traceInfos = new HashSet<TraceInfo>();
41 }
42
43 @Override
44 public String toString() {
45 if (tag != null)
46 return toStringCore() + "->" + getTraceInfoPatternsEnumerated() + "{" + tag.toString() + "}";
47 else
48 return toStringCore() + "->" + getTraceInfoPatternsEnumerated();
49 }
50
51 /**
52 * clients should override this to append before the tag / trace indicators
53 */
54 protected String toStringCore() {
55 return "[" + nodeId + "]" + getClass().getSimpleName();
56 }
57
58 @Override
59 public ReteContainer getContainer() {
60 return reteContainer;
61 }
62
63 @Override
64 public long getNodeId() {
65 return nodeId;
66 }
67
68 @Override
69 public Object getTag() {
70 return tag;
71 }
72
73 @Override
74 public void setTag(Object tag) {
75 this.tag = tag;
76 }
77
78 @Override
79 public Set<TraceInfo> getTraceInfos() {
80 return Collections.unmodifiableSet(traceInfos);
81 }
82
83 @Override
84 public void assignTraceInfo(TraceInfo traceInfo) {
85 traceInfos.add(traceInfo);
86 traceInfo.assignNode(this);
87 }
88
89 @Override
90 public void acceptPropagatedTraceInfo(TraceInfo traceInfo) {
91 assignTraceInfo(traceInfo);
92 }
93
94 /**
95 * Descendants should use this in e.g. logging
96 */
97 protected String getTraceInfoPatternsEnumerated() {
98 TreeSet<String> patternNames = new TreeSet<String>();
99 for (TraceInfo trInfo : traceInfos) {
100 if (trInfo instanceof PatternTraceInfo) {
101 final String pName = ((PatternTraceInfo) trInfo).getPatternName();
102 patternNames.add(pName);
103 }
104 }
105 return patternNames.toString();
106 }
107
108} \ 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..b261d19d
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java
@@ -0,0 +1,171 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.viatra.runtime.rete.network;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.rete.aggregation.IndexerBasedAggregatorNode;
14import tools.refinery.viatra.runtime.rete.boundary.InputConnector;
15import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode;
16import tools.refinery.viatra.runtime.rete.index.DualInputNode;
17import tools.refinery.viatra.runtime.rete.index.Indexer;
18import tools.refinery.viatra.runtime.rete.index.IterableIndexer;
19import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
20import tools.refinery.viatra.runtime.rete.recipes.*;
21import tools.refinery.viatra.runtime.rete.remote.Address;
22import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
23
24import java.util.ArrayList;
25import java.util.Collection;
26import java.util.List;
27
28/**
29 * Class responsible for connecting freshly instantiating Rete nodes to their parents.
30 *
31 * @author Bergmann Gabor
32 *
33 */
34class ConnectionFactory {
35 ReteContainer reteContainer;
36
37 public ConnectionFactory(ReteContainer reteContainer) {
38 super();
39 this.reteContainer = reteContainer;
40 }
41
42 // TODO move to node implementation instead?
43 private boolean isStateful(ReteNodeRecipe recipe) {
44 return recipe instanceof ProjectionIndexerRecipe || recipe instanceof IndexerBasedAggregatorRecipe
45 || recipe instanceof SingleColumnAggregatorRecipe || recipe instanceof ExpressionEnforcerRecipe
46 || recipe instanceof TransitiveClosureRecipe || recipe instanceof ProductionRecipe
47 || recipe instanceof UniquenessEnforcerRecipe || recipe instanceof RelationEvaluationRecipe;
48
49 }
50
51 /**
52 * PRE: nodes for parent recipes must already be created and registered
53 * <p>
54 * PRE: must not be an input node (for which {@link InputConnector} is responsible)
55 */
56 public void connectToParents(RecipeTraceInfo recipeTrace, Node freshNode) {
57 final ReteNodeRecipe recipe = recipeTrace.getRecipe();
58 if (recipe instanceof ConstantRecipe) {
59 // NO-OP
60 } else if (recipe instanceof InputRecipe) {
61 throw new IllegalArgumentException(
62 ConnectionFactory.class.getSimpleName() + " not intended for input connection: " + recipe);
63 } else if (recipe instanceof SingleParentNodeRecipe) {
64 final Receiver receiver = (Receiver) freshNode;
65 ReteNodeRecipe parentRecipe = ((SingleParentNodeRecipe) recipe).getParent();
66 connectToParent(recipe, receiver, parentRecipe);
67 } else if (recipe instanceof RelationEvaluationRecipe) {
68 List<ReteNodeRecipe> parentRecipes = ((MultiParentNodeRecipe) recipe).getParents();
69 List<Supplier> parentSuppliers = new ArrayList<Supplier>();
70 for (final ReteNodeRecipe parentRecipe : parentRecipes) {
71 parentSuppliers.add(getSupplierForRecipe(parentRecipe));
72 }
73 ((RelationEvaluatorNode) freshNode).connectToParents(parentSuppliers);
74 } else if (recipe instanceof BetaRecipe) {
75 final DualInputNode beta = (DualInputNode) freshNode;
76 final ArrayList<RecipeTraceInfo> parentTraces = new ArrayList<RecipeTraceInfo>(
77 recipeTrace.getParentRecipeTraces());
78 Slots slots = avoidActiveNodeConflict(parentTraces.get(0), parentTraces.get(1));
79 beta.connectToIndexers(slots.primary, slots.secondary);
80 } else if (recipe instanceof IndexerBasedAggregatorRecipe) {
81 final IndexerBasedAggregatorNode aggregator = (IndexerBasedAggregatorNode) freshNode;
82 final IndexerBasedAggregatorRecipe aggregatorRecipe = (IndexerBasedAggregatorRecipe) recipe;
83 aggregator.initializeWith((ProjectionIndexer) resolveIndexer(aggregatorRecipe.getParent()));
84 } else if (recipe instanceof MultiParentNodeRecipe) {
85 final Receiver receiver = (Receiver) freshNode;
86 List<ReteNodeRecipe> parentRecipes = ((MultiParentNodeRecipe) recipe).getParents();
87 for (ReteNodeRecipe parentRecipe : parentRecipes) {
88 connectToParent(recipe, receiver, parentRecipe);
89 }
90 }
91 }
92
93 private Indexer resolveIndexer(final IndexerRecipe indexerRecipe) {
94 final Address<? extends Node> address = reteContainer.getNetwork().getExistingNodeByRecipe(indexerRecipe);
95 return (Indexer) reteContainer.resolveLocal(address);
96 }
97
98 private void connectToParent(ReteNodeRecipe recipe, Receiver freshNode, ReteNodeRecipe parentRecipe) {
99 final Supplier parentSupplier = getSupplierForRecipe(parentRecipe);
100
101 // special synch
102 if (freshNode instanceof ReinitializedNode) {
103 Collection<Tuple> tuples = new ArrayList<Tuple>();
104 parentSupplier.pullInto(tuples, true);
105 ((ReinitializedNode) freshNode).reinitializeWith(tuples);
106 reteContainer.connect(parentSupplier, freshNode);
107 } else { // default case
108 // stateless nodes do not have to be synced with contents UNLESS they already have children (recursive
109 // corner case)
110 if (isStateful(recipe)
111 || ((freshNode instanceof Supplier) && !((Supplier) freshNode).getReceivers().isEmpty())) {
112 reteContainer.connectAndSynchronize(parentSupplier, freshNode);
113 } else {
114 // stateless node, no synch
115 reteContainer.connect(parentSupplier, freshNode);
116 }
117 }
118 }
119
120 private Supplier getSupplierForRecipe(ReteNodeRecipe recipe) {
121 @SuppressWarnings("unchecked")
122 final Address<? extends Supplier> parentAddress = (Address<? extends Supplier>) reteContainer.getNetwork()
123 .getExistingNodeByRecipe(recipe);
124 final Supplier supplier = reteContainer.getProvisioner().asSupplier(parentAddress);
125 return supplier;
126 }
127
128 /**
129 * If two indexers share their active node, joining them via DualInputNode is error-prone. Exception: coincidence of
130 * the two indexers is supported.
131 *
132 * @return a replacement for the secondary Indexers, if needed
133 */
134 private Slots avoidActiveNodeConflict(final RecipeTraceInfo primarySlot, final RecipeTraceInfo secondarySlot) {
135 Slots result = new Slots() {
136 {
137 primary = (IterableIndexer) resolveIndexer((ProjectionIndexerRecipe) primarySlot.getRecipe());
138 secondary = resolveIndexer((IndexerRecipe) secondarySlot.getRecipe());
139 }
140 };
141 if (activeNodeConflict(result.primary, result.secondary))
142 if (result.secondary instanceof IterableIndexer)
143 result.secondary = resolveActiveIndexer(secondarySlot);
144 else
145 result.primary = (IterableIndexer) resolveActiveIndexer(primarySlot);
146 return result;
147 }
148
149 private Indexer resolveActiveIndexer(final RecipeTraceInfo inactiveIndexerTrace) {
150 final RecipeTraceInfo activeIndexerTrace = reteContainer.getProvisioner()
151 .accessActiveIndexer(inactiveIndexerTrace);
152 reteContainer.getProvisioner().getOrCreateNodeByRecipe(activeIndexerTrace);
153 return resolveIndexer((ProjectionIndexerRecipe) activeIndexerTrace.getRecipe());
154 }
155
156 private static class Slots {
157 IterableIndexer primary;
158 Indexer secondary;
159 }
160
161 /**
162 * If two indexers share their active node, joining them via DualInputNode is error-prone. Exception: coincidence of
163 * the two indexers is supported.
164 *
165 * @return true if there is a conflict of active nodes.
166 */
167 private boolean activeNodeConflict(Indexer primarySlot, Indexer secondarySlot) {
168 return !primarySlot.equals(secondarySlot) && primarySlot.getActiveNode().equals(secondarySlot.getActiveNode());
169 }
170
171}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network;
10
11import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
12
13/**
14 * @author Gabor Bergmann
15 * @since 1.7
16 */
17public interface IGroupable {
18
19 /**
20 * @return the current group of the mailbox
21 * @since 1.7
22 */
23 CommunicationGroup getCurrentGroup();
24
25 /**
26 * Sets the current group of the mailbox
27 * @since 1.7
28 */
29 void setCurrentGroup(CommunicationGroup group);
30
31} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.network;
11
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.List;
16import java.util.Map;
17import java.util.Map.Entry;
18import java.util.Set;
19import java.util.concurrent.locks.Lock;
20import java.util.concurrent.locks.ReadWriteLock;
21import java.util.concurrent.locks.ReentrantReadWriteLock;
22
23import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
24import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
25import tools.refinery.viatra.runtime.matchers.util.Direction;
26import tools.refinery.viatra.runtime.rete.boundary.InputConnector;
27import tools.refinery.viatra.runtime.rete.matcher.ReteEngine;
28import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
29import tools.refinery.viatra.runtime.rete.remote.Address;
30import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
31import tools.refinery.viatra.runtime.rete.util.Options;
32
33/**
34 * @author Gabor Bergmann
35 *
36 */
37public class Network {
38 final int threads;
39
40 protected ArrayList<ReteContainer> containers;
41 ReteContainer headContainer;
42 private int firstContainer = 0;
43 private int nextContainer = 0;
44
45 // the following fields exist only if threads > 0
46 protected Map<ReteContainer, Long> globalTerminationCriteria = null;
47 protected Map<ReteContainer, Long> reportedClocks = null;
48 protected Lock updateLock = null; // grab during normal update operations
49 protected Lock structuralChangeLock = null; // grab if the network structure
50 // is to
51 // be changed
52
53 // Knowledge of the outside world
54 private ReteEngine engine;
55 protected NodeFactory nodeFactory;
56 protected InputConnector inputConnector;
57
58 // Node and recipe administration
59 // incl. addresses for existing nodes by recipe (where available)
60 // Maintained by NodeProvisioner of each container
61 Map<ReteNodeRecipe, Address<? extends Node>> nodesByRecipe = CollectionsFactory.createMap();
62 Set<RecipeTraceInfo> recipeTraces = CollectionsFactory.createSet();
63
64 /**
65 * @throws IllegalStateException
66 * if no node has been constructed for the recipe
67 */
68 public synchronized Address<? extends Node> getExistingNodeByRecipe(ReteNodeRecipe recipe) {
69 final Address<? extends Node> node = nodesByRecipe.get(recipe);
70 if (node == null)
71 throw new IllegalStateException(String.format("Rete node for recipe %s not constructed yet.", recipe));
72 return node;
73 }
74
75 /**
76 * @return null if no node has been constructed for the recipe
77 */
78 public synchronized Address<? extends Node> getNodeByRecipeIfExists(ReteNodeRecipe recipe) {
79 final Address<? extends Node> node = nodesByRecipe.get(recipe);
80 return node;
81 }
82
83 /**
84 * @param threads
85 * the number of threads to operate the network with; 0 means single-threaded operation, 1 starts an
86 * asynchronous thread to operate the RETE net, >1 uses multiple RETE containers.
87 */
88 public Network(int threads, ReteEngine engine) {
89 super();
90 this.threads = threads;
91 this.engine = engine;
92 this.inputConnector = new InputConnector(this);
93 this.nodeFactory = new NodeFactory(engine.getLogger());
94
95 containers = new ArrayList<ReteContainer>();
96 firstContainer = (threads > 1) ? Options.firstFreeContainer : 0; // NOPMD
97 nextContainer = firstContainer;
98
99 if (threads > 0) {
100 globalTerminationCriteria = CollectionsFactory.createMap();
101 reportedClocks = CollectionsFactory.createMap();
102 ReadWriteLock rwl = new ReentrantReadWriteLock();
103 updateLock = rwl.readLock();
104 structuralChangeLock = rwl.writeLock();
105 for (int i = 0; i < threads; ++i)
106 containers.add(new ReteContainer(this, true));
107 } else
108 containers.add(new ReteContainer(this, false));
109
110 headContainer = containers.get(0);
111 }
112
113 /**
114 * Kills this Network along with all containers and message consumption cycles.
115 */
116 public void kill() {
117 for (ReteContainer container : containers) {
118 container.kill();
119 }
120 containers.clear();
121 }
122
123 /**
124 * Returns the head container, that is guaranteed to reside in the same JVM as the Network object.
125 */
126 public ReteContainer getHeadContainer() {
127 return headContainer;
128 }
129
130 /**
131 * Returns the next container in round-robin fashion. Configurable not to yield head container.
132 */
133 public ReteContainer getNextContainer() {
134 if (nextContainer >= containers.size())
135 nextContainer = firstContainer;
136 return containers.get(nextContainer++);
137 }
138
139 /**
140 * Internal message delivery method.
141 *
142 * @pre threads > 0
143 */
144 private void sendUpdate(Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) {
145 ReteContainer affectedContainer = receiver.getContainer();
146 synchronized (globalTerminationCriteria) {
147 long newCriterion = affectedContainer.sendUpdateToLocalAddress(receiver, direction, updateElement);
148 terminationCriterion(affectedContainer, newCriterion);
149 }
150 }
151
152 /**
153 * Internal message delivery method for single-threaded operation
154 *
155 * @pre threads == 0
156 */
157 private void sendUpdateSingleThreaded(Address<? extends Receiver> receiver, Direction direction,
158 Tuple updateElement) {
159 ReteContainer affectedContainer = receiver.getContainer();
160 affectedContainer.sendUpdateToLocalAddressSingleThreaded(receiver, direction, updateElement);
161 }
162
163 /**
164 * Internal message delivery method.
165 *
166 * @pre threads > 0
167 */
168 private void sendUpdates(Address<? extends Receiver> receiver, Direction direction,
169 Collection<Tuple> updateElements) {
170 if (updateElements.isEmpty())
171 return;
172 ReteContainer affectedContainer = receiver.getContainer();
173 synchronized (globalTerminationCriteria) {
174 long newCriterion = affectedContainer.sendUpdatesToLocalAddress(receiver, direction, updateElements);
175 terminationCriterion(affectedContainer, newCriterion);
176 }
177 }
178
179 /**
180 * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The node may
181 * reside in any of the containers associated with this network. To be called from a user thread during normal
182 * operation, NOT during construction.
183 *
184 * @since 2.4
185 */
186 public void sendExternalUpdate(Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) {
187 if (threads > 0) {
188 try {
189 updateLock.lock();
190 sendUpdate(receiver, direction, updateElement);
191 } finally {
192 updateLock.unlock();
193 }
194 } else {
195 sendUpdateSingleThreaded(receiver, direction, updateElement);
196 // getHeadContainer().
197 }
198 }
199
200 /**
201 * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The node may
202 * reside in any of the containers associated with this network. To be called from a user thread during
203 * construction.
204 *
205 * @pre: structuralChangeLock MUST be grabbed by the sequence (but not necessarily this thread, as the sequence may
206 * span through network calls, that's why it's not enforced here )
207 *
208 * @return the value of the target container's clock at the time when the message was accepted into its message
209 * queue
210 * @since 2.4
211 */
212 public void sendConstructionUpdate(Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) {
213 // structuralChangeLock.lock();
214 if (threads > 0)
215 sendUpdate(receiver, direction, updateElement);
216 else
217 receiver.getContainer().sendUpdateToLocalAddressSingleThreaded(receiver, direction, updateElement);
218 // structuralChangeLock.unlock();
219 }
220
221 /**
222 * Sends multiple update messages atomically to the receiver node, indicating a newly found or lost partial
223 * matching. The node may reside in any of the containers associated with this network. To be called from a user
224 * thread during construction.
225 *
226 * @pre: structuralChangeLock MUST be grabbed by the sequence (but not necessarily this thread, as the sequence may
227 * span through network calls, that's why it's not enforced here )
228 *
229 * @since 2.4
230 */
231 public void sendConstructionUpdates(Address<? extends Receiver> receiver, Direction direction,
232 Collection<Tuple> updateElements) {
233 // structuralChangeLock.lock();
234 if (threads > 0)
235 sendUpdates(receiver, direction, updateElements);
236 else
237 receiver.getContainer().sendUpdatesToLocalAddressSingleThreaded(receiver, direction, updateElements);
238 // structuralChangeLock.unlock();
239 }
240
241 /**
242 * Establishes connection between a supplier and a receiver node, regardless which container they are in. Not to be
243 * called remotely, because this method enforces the structural lock.
244 *
245 * @param supplier
246 * @param receiver
247 * @param synchronise
248 * indicates whether the receiver should be synchronised to the current contents of the supplier
249 */
250 public void connectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver,
251 boolean synchronise) {
252 try {
253 if (threads > 0)
254 structuralChangeLock.lock();
255 receiver.getContainer().connectRemoteNodes(supplier, receiver, synchronise);
256 } finally {
257 if (threads > 0)
258 structuralChangeLock.unlock();
259 }
260 }
261
262 /**
263 * Severs connection between a supplier and a receiver node, regardless which container they are in. Not to be
264 * called remotely, because this method enforces the structural lock.
265 *
266 * @param supplier
267 * @param receiver
268 * @param desynchronise
269 * indicates whether the current contents of the supplier should be subtracted from the receiver
270 */
271 public void disconnectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver,
272 boolean desynchronise) {
273 try {
274 if (threads > 0)
275 structuralChangeLock.lock();
276 receiver.getContainer().disconnectRemoteNodes(supplier, receiver, desynchronise);
277 } finally {
278 if (threads > 0)
279 structuralChangeLock.unlock();
280 }
281 }
282
283 /**
284 * Containers use this method to report whenever they run out of messages in their queue.
285 *
286 * To be called from the thread of the reporting container.
287 *
288 * @pre threads > 0.
289 * @param reportingContainer
290 * the container reporting the emptiness of its message queue.
291 * @param clock
292 * the value of the container's clock when reporting.
293 * @param localTerminationCriteria
294 * the latest clock values this container has received from other containers since the last time it
295 * reported termination.
296 */
297 void reportLocalUpdateTermination(ReteContainer reportingContainer, long clock,
298 Map<ReteContainer, Long> localTerminationCriteria) {
299 synchronized (globalTerminationCriteria) {
300 for (Entry<ReteContainer, Long> criterion : localTerminationCriteria.entrySet()) {
301 terminationCriterion(criterion.getKey(), criterion.getValue());
302 }
303
304 reportedClocks.put(reportingContainer, clock);
305 Long criterion = globalTerminationCriteria.get(reportingContainer);
306 if (criterion != null && criterion < clock)
307 globalTerminationCriteria.remove(reportingContainer);
308
309 if (globalTerminationCriteria.isEmpty())
310 globalTerminationCriteria.notifyAll();
311 }
312 }
313
314 /**
315 * @pre threads > 0
316 */
317 private void terminationCriterion(ReteContainer affectedContainer, long newCriterion) {
318 synchronized (globalTerminationCriteria) {
319 Long oldCriterion = globalTerminationCriteria.get(affectedContainer);
320 Long oldClock = reportedClocks.get(affectedContainer);
321 long relevantClock = oldClock == null ? 0 : oldClock;
322 if ((relevantClock <= newCriterion) && (oldCriterion == null || oldCriterion < newCriterion)) {
323 globalTerminationCriteria.put(affectedContainer, newCriterion);
324 }
325 }
326 }
327
328 /**
329 * Waits until all rete update operations are settled in all containers. Returns immediately, if no updates are
330 * pending.
331 *
332 * To be called from any user thread.
333 */
334 public void waitForReteTermination() {
335 if (threads > 0) {
336 synchronized (globalTerminationCriteria) {
337 while (!globalTerminationCriteria.isEmpty()) {
338 try {
339 globalTerminationCriteria.wait();
340 } catch (InterruptedException e) {
341
342 }
343 }
344 }
345 } else
346 headContainer.deliverMessagesSingleThreaded();
347 }
348
349 /**
350 * Waits to execute action until all rete update operations are settled in all containers. Runs action and returns
351 * immediately, if no updates are pending. The given action is guaranteed to be run when the terminated state still
352 * persists.
353 *
354 * @param action
355 * the action to be run when reaching the steady-state.
356 *
357 * To be called from any user thread.
358 */
359 public void waitForReteTermination(Runnable action) {
360 if (threads > 0) {
361 synchronized (globalTerminationCriteria) {
362 while (!globalTerminationCriteria.isEmpty()) {
363 try {
364 globalTerminationCriteria.wait();
365 } catch (InterruptedException e) {
366
367 }
368 }
369 action.run();
370 }
371 } else {
372 headContainer.deliverMessagesSingleThreaded();
373 action.run();
374 }
375
376 }
377
378 /**
379 * @return an unmodifiable set of known recipe traces
380 */
381 public Set<RecipeTraceInfo> getRecipeTraces() {
382 return Collections.unmodifiableSet(recipeTraces);
383 }
384
385 /**
386 * @return an unmodifiable list of containers
387 */
388 public List<ReteContainer> getContainers() {
389 return Collections.unmodifiableList(containers);
390 }
391
392 public Lock getStructuralChangeLock() {
393 return structuralChangeLock;
394 }
395
396 public NodeFactory getNodeFactory() {
397 return nodeFactory;
398 }
399
400 public InputConnector getInputConnector() {
401 return inputConnector;
402 }
403
404 public ReteEngine getEngine() {
405 return engine;
406 }
407
408}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network;
10
11import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
12
13/**
14 * {@link Node}s implementing this interface are sensitive to changes in the dependency graph maintained by the
15 * {@link CommunicationTracker}. The {@link CommunicationTracker} notifies these nodes whenever the SCC of this node is
16 * affected by changes to the dependency graph. Depending on whether this node is contained in a recursive group or not,
17 * it may behave differently, and the {@link NetworkStructureChangeSensitiveNode#networkStructureChanged()} method can
18 * be used to perform changes in behavior.
19 *
20 * @author Tamas Szabo
21 * @since 2.3
22 */
23public interface NetworkStructureChangeSensitiveNode extends Node {
24
25 /**
26 * At the time of the invocation, the dependency graph has already been updated.
27 */
28 public void networkStructureChanged();
29
30}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.network;
11
12import java.util.Set;
13
14import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
15import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
16
17/**
18 * A node of a rete network, should be uniquely identified by network and nodeId. NodeId can be requested by registering
19 * at the Network on construction.
20 *
21 * @author Gabor Bergmann
22 */
23public interface Node {
24 /**
25 * @return the network this node belongs to.
26 */
27 ReteContainer getContainer();
28
29 /**
30 * @return the identifier unique to this node within the network.
31 */
32 long getNodeId();
33
34 /**
35 * Assigns a descriptive tag to the node
36 */
37 void setTag(Object tag);
38
39 /**
40 * @return the tag of the node
41 */
42 Object getTag();
43
44 /**
45 * @return unmodifiable view of the list of traceability infos assigned to this node
46 */
47 Set<TraceInfo> getTraceInfos();
48
49 /**
50 * assigns new traceability info to this node
51 */
52 void assignTraceInfo(TraceInfo traceInfo);
53 /**
54 * accepts traceability info propagated to this node
55 */
56 void acceptPropagatedTraceInfo(TraceInfo traceInfo);
57
58 default CommunicationTracker getCommunicationTracker() {
59 return getContainer().getCommunicationTracker();
60 }
61
62}
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..3e4ea4e0
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java
@@ -0,0 +1,376 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10package tools.refinery.viatra.runtime.rete.network;
11
12import org.apache.log4j.Logger;
13import org.eclipse.emf.common.util.EMap;
14import tools.refinery.viatra.runtime.rete.itc.alg.representative.RepresentativeElectionAlgorithm;
15import tools.refinery.viatra.runtime.rete.itc.alg.representative.StronglyConnectedComponentAlgorithm;
16import tools.refinery.viatra.runtime.rete.itc.alg.representative.WeaklyConnectedComponentAlgorithm;
17import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
18import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
19import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator;
20import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
21import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
22import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
23import tools.refinery.viatra.runtime.rete.aggregation.ColumnAggregatorNode;
24import tools.refinery.viatra.runtime.rete.aggregation.CountNode;
25import tools.refinery.viatra.runtime.rete.aggregation.IAggregatorNode;
26import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode;
27import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode;
28import tools.refinery.viatra.runtime.rete.aggregation.timely.FirstOnlyParallelTimelyColumnAggregatorNode;
29import tools.refinery.viatra.runtime.rete.aggregation.timely.FirstOnlySequentialTimelyColumnAggregatorNode;
30import tools.refinery.viatra.runtime.rete.boundary.ExternalInputEnumeratorNode;
31import tools.refinery.viatra.runtime.rete.boundary.ExternalInputStatelessFilterNode;
32import tools.refinery.viatra.runtime.rete.eval.EvaluatorCore;
33import tools.refinery.viatra.runtime.rete.eval.MemorylessEvaluatorNode;
34import tools.refinery.viatra.runtime.rete.eval.OutputCachingEvaluatorNode;
35import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode;
36import tools.refinery.viatra.runtime.rete.index.ExistenceNode;
37import tools.refinery.viatra.runtime.rete.index.Indexer;
38import tools.refinery.viatra.runtime.rete.index.JoinNode;
39import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
40import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.AggregatorArchitecture;
41import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
42import tools.refinery.viatra.runtime.rete.misc.ConstantNode;
43import tools.refinery.viatra.runtime.rete.recipes.*;
44import tools.refinery.viatra.runtime.rete.single.*;
45import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
46
47import java.util.HashMap;
48import java.util.List;
49import java.util.Map;
50
51/**
52 * Factory for instantiating Rete nodes. The created nodes are not connected to the network yet.
53 *
54 * @author Bergmann Gabor
55 *
56 */
57class NodeFactory {
58 Logger logger;
59
60 public NodeFactory(Logger logger) {
61 super();
62 this.logger = logger;
63 }
64
65 /**
66 * PRE: parent node must already be created
67 */
68 public Indexer createIndexer(ReteContainer reteContainer, IndexerRecipe recipe, Supplier parentNode,
69 TraceInfo... traces) {
70
71 if (recipe instanceof ProjectionIndexerRecipe) {
72 return parentNode.constructIndex(toMask(recipe.getMask()), traces);
73 // already traced
74 } else if (recipe instanceof AggregatorIndexerRecipe) {
75 int indexOfAggregateResult = recipe.getParent().getArity();
76 int resultPosition = recipe.getMask().getSourceIndices().lastIndexOf(indexOfAggregateResult);
77
78 IAggregatorNode aggregatorNode = (IAggregatorNode) parentNode;
79 final Indexer result = (resultPosition == -1) ? aggregatorNode.getAggregatorOuterIndexer()
80 : aggregatorNode.getAggregatorOuterIdentityIndexer(resultPosition);
81
82 for (TraceInfo traceInfo : traces)
83 result.assignTraceInfo(traceInfo);
84 return result;
85 } else
86 throw new IllegalArgumentException("Unkown Indexer recipe: " + recipe);
87 }
88
89 /**
90 * PRE: recipe is not an indexer recipe.
91 */
92 public Supplier createNode(ReteContainer reteContainer, ReteNodeRecipe recipe, TraceInfo... traces) {
93 if (recipe instanceof IndexerRecipe)
94 throw new IllegalArgumentException("Indexers are not created by NodeFactory: " + recipe);
95
96 Supplier result = instantiateNodeDispatch(reteContainer, recipe);
97 for (TraceInfo traceInfo : traces)
98 result.assignTraceInfo(traceInfo);
99 return result;
100 }
101
102 private Supplier instantiateNodeDispatch(ReteContainer reteContainer, ReteNodeRecipe recipe) {
103
104 // Parentless
105
106 if (recipe instanceof ConstantRecipe)
107 return instantiateNode(reteContainer, (ConstantRecipe) recipe);
108 if (recipe instanceof InputRecipe)
109 return instantiateNode(reteContainer, (InputRecipe) recipe);
110
111 // SingleParentNodeRecipe
112
113 // if (recipe instanceof ProjectionIndexer)
114 // return instantiateNode((ProjectionIndexer)recipe);
115 if (recipe instanceof InputFilterRecipe)
116 return instantiateNode(reteContainer, (InputFilterRecipe) recipe);
117 if (recipe instanceof InequalityFilterRecipe)
118 return instantiateNode(reteContainer, (InequalityFilterRecipe) recipe);
119 if (recipe instanceof EqualityFilterRecipe)
120 return instantiateNode(reteContainer, (EqualityFilterRecipe) recipe);
121 if (recipe instanceof TransparentRecipe)
122 return instantiateNode(reteContainer, (TransparentRecipe) recipe);
123 if (recipe instanceof TrimmerRecipe)
124 return instantiateNode(reteContainer, (TrimmerRecipe) recipe);
125 if (recipe instanceof TransitiveClosureRecipe)
126 return instantiateNode(reteContainer, (TransitiveClosureRecipe) recipe);
127 if (recipe instanceof RepresentativeElectionRecipe)
128 return instantiateNode(reteContainer, (RepresentativeElectionRecipe) recipe);
129 if (recipe instanceof RelationEvaluationRecipe)
130 return instantiateNode(reteContainer, (RelationEvaluationRecipe) recipe);
131 if (recipe instanceof ExpressionEnforcerRecipe)
132 return instantiateNode(reteContainer, (ExpressionEnforcerRecipe) recipe);
133 if (recipe instanceof CountAggregatorRecipe)
134 return instantiateNode(reteContainer, (CountAggregatorRecipe) recipe);
135 if (recipe instanceof SingleColumnAggregatorRecipe)
136 return instantiateNode(reteContainer, (SingleColumnAggregatorRecipe) recipe);
137 if (recipe instanceof DiscriminatorDispatcherRecipe)
138 return instantiateNode(reteContainer, (DiscriminatorDispatcherRecipe) recipe);
139 if (recipe instanceof DiscriminatorBucketRecipe)
140 return instantiateNode(reteContainer, (DiscriminatorBucketRecipe) recipe);
141
142 // MultiParentNodeRecipe
143 if (recipe instanceof UniquenessEnforcerRecipe)
144 return instantiateNode(reteContainer, (UniquenessEnforcerRecipe) recipe);
145 if (recipe instanceof ProductionRecipe)
146 return instantiateNode(reteContainer, (ProductionRecipe) recipe);
147
148 // BetaNodeRecipe
149 if (recipe instanceof JoinRecipe)
150 return instantiateNode(reteContainer, (JoinRecipe) recipe);
151 if (recipe instanceof SemiJoinRecipe)
152 return instantiateNode(reteContainer, (SemiJoinRecipe) recipe);
153 if (recipe instanceof AntiJoinRecipe)
154 return instantiateNode(reteContainer, (AntiJoinRecipe) recipe);
155
156 // ... else
157 throw new IllegalArgumentException("Unsupported recipe type: " + recipe);
158 }
159
160 // INSTANTIATION for recipe types
161
162 private Supplier instantiateNode(ReteContainer reteContainer, InputRecipe recipe) {
163 return new ExternalInputEnumeratorNode(reteContainer);
164 }
165
166 private Supplier instantiateNode(ReteContainer reteContainer, InputFilterRecipe recipe) {
167 return new ExternalInputStatelessFilterNode(reteContainer, toMaskOrNull(recipe.getMask()));
168 }
169
170 private Supplier instantiateNode(ReteContainer reteContainer, CountAggregatorRecipe recipe) {
171 return new CountNode(reteContainer);
172 }
173
174 private Supplier instantiateNode(ReteContainer reteContainer, TransparentRecipe recipe) {
175 return new TransparentNode(reteContainer);
176 }
177
178 private Supplier instantiateNode(ReteContainer reteContainer, ExpressionEnforcerRecipe recipe) {
179 final IExpressionEvaluator evaluator = toIExpressionEvaluator(recipe.getExpression());
180 final Map<String, Integer> posMapping = toStringIndexMap(recipe.getMappedIndices());
181 final int sourceTupleWidth = recipe.getParent().getArity();
182 EvaluatorCore core = null;
183 if (recipe instanceof CheckRecipe) {
184 core = new EvaluatorCore.PredicateEvaluatorCore(logger, evaluator, posMapping, sourceTupleWidth);
185 } else if (recipe instanceof EvalRecipe) {
186 final boolean isUnwinding = ((EvalRecipe) recipe).isUnwinding();
187 core = new EvaluatorCore.FunctionEvaluatorCore(logger, evaluator, posMapping, sourceTupleWidth, isUnwinding);
188 } else {
189 throw new IllegalArgumentException("Unhandled expression enforcer recipe: " + recipe.getClass() + "!");
190 }
191 if (recipe.isCacheOutput()) {
192 return new OutputCachingEvaluatorNode(reteContainer, core);
193 } else {
194 return new MemorylessEvaluatorNode(reteContainer, core);
195 }
196 }
197
198 @SuppressWarnings({ "rawtypes", "unchecked" })
199 private Supplier instantiateNode(ReteContainer reteContainer, SingleColumnAggregatorRecipe recipe) {
200 final IMultisetAggregationOperator operator = recipe.getMultisetAggregationOperator();
201 TupleMask coreMask = null;
202 if (recipe.getOptionalMonotonicityInfo() != null) {
203 coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask());
204 } else {
205 coreMask = toMask(recipe.getGroupByMask());
206 }
207
208 if (reteContainer.isTimelyEvaluation()) {
209 final TimelyConfiguration timelyConfiguration = reteContainer.getTimelyConfiguration();
210 final AggregatorArchitecture aggregatorArchitecture = timelyConfiguration.getAggregatorArchitecture();
211 final TimelineRepresentation timelineRepresentation = timelyConfiguration.getTimelineRepresentation();
212
213 TupleMask posetMask = null;
214
215 if (recipe.getOptionalMonotonicityInfo() != null) {
216 posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask());
217 } else {
218 final int aggregatedColumn = recipe.getAggregableIndex();
219 posetMask = TupleMask.selectSingle(aggregatedColumn, coreMask.sourceWidth);
220 }
221
222 if (timelineRepresentation == TimelineRepresentation.FIRST_ONLY
223 && aggregatorArchitecture == AggregatorArchitecture.SEQUENTIAL) {
224 return new FirstOnlySequentialTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask);
225 } else if (timelineRepresentation == TimelineRepresentation.FIRST_ONLY
226 && aggregatorArchitecture == AggregatorArchitecture.PARALLEL) {
227 return new FirstOnlyParallelTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask);
228 } else if (timelineRepresentation == TimelineRepresentation.FAITHFUL
229 && aggregatorArchitecture == AggregatorArchitecture.SEQUENTIAL) {
230 return new FaithfulSequentialTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask);
231 } else if (timelineRepresentation == TimelineRepresentation.FAITHFUL
232 && aggregatorArchitecture == AggregatorArchitecture.PARALLEL) {
233 return new FaithfulParallelTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask);
234 } else {
235 throw new IllegalArgumentException("Unsupported timely configuration!");
236 }
237 } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) {
238 final TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask());
239 final IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo()
240 .getPosetComparator();
241 return new ColumnAggregatorNode(reteContainer, operator, recipe.isDeleteRederiveEvaluation(), coreMask,
242 posetMask, posetComparator);
243 } else {
244 final int aggregatedColumn = recipe.getAggregableIndex();
245 return new ColumnAggregatorNode(reteContainer, operator, coreMask, aggregatedColumn);
246 }
247 }
248
249 private Supplier instantiateNode(ReteContainer reteContainer, TransitiveClosureRecipe recipe) {
250 return new TransitiveClosureNode(reteContainer);
251 }
252
253 private Supplier instantiateNode(ReteContainer reteContainer, RepresentativeElectionRecipe recipe) {
254 RepresentativeElectionAlgorithm.Factory algorithmFactory = switch (recipe.getConnectivity()) {
255 case STRONG -> StronglyConnectedComponentAlgorithm::new;
256 case WEAK -> WeaklyConnectedComponentAlgorithm::new;
257 };
258 return new RepresentativeElectionNode(reteContainer, algorithmFactory);
259 }
260
261 private Supplier instantiateNode(ReteContainer reteContainer, RelationEvaluationRecipe recipe) {
262 return new RelationEvaluatorNode(reteContainer, toIRelationEvaluator(recipe.getEvaluator()));
263 }
264
265 private Supplier instantiateNode(ReteContainer reteContainer, ProductionRecipe recipe) {
266 if (reteContainer.isTimelyEvaluation()) {
267 return new TimelyProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices()));
268 } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) {
269 TupleMask coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask());
270 TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask());
271 IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo()
272 .getPosetComparator();
273 return new DefaultProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices()),
274 recipe.isDeleteRederiveEvaluation(), coreMask, posetMask, posetComparator);
275 } else {
276 return new DefaultProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices()),
277 recipe.isDeleteRederiveEvaluation());
278 }
279 }
280
281 private Supplier instantiateNode(ReteContainer reteContainer, UniquenessEnforcerRecipe recipe) {
282 if (reteContainer.isTimelyEvaluation()) {
283 return new TimelyUniquenessEnforcerNode(reteContainer, recipe.getArity());
284 } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) {
285 TupleMask coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask());
286 TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask());
287 IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo()
288 .getPosetComparator();
289 return new UniquenessEnforcerNode(reteContainer, recipe.getArity(), recipe.isDeleteRederiveEvaluation(),
290 coreMask, posetMask, posetComparator);
291 } else {
292 return new UniquenessEnforcerNode(reteContainer, recipe.getArity(), recipe.isDeleteRederiveEvaluation());
293 }
294 }
295
296 private Supplier instantiateNode(ReteContainer reteContainer, ConstantRecipe recipe) {
297 final List<Object> constantValues = recipe.getConstantValues();
298 final Object[] constantArray = constantValues.toArray(new Object[constantValues.size()]);
299 return new ConstantNode(reteContainer, Tuples.flatTupleOf(constantArray));
300 }
301
302 private Supplier instantiateNode(ReteContainer reteContainer, DiscriminatorBucketRecipe recipe) {
303 return new DiscriminatorBucketNode(reteContainer, recipe.getBucketKey());
304 }
305
306 private Supplier instantiateNode(ReteContainer reteContainer, DiscriminatorDispatcherRecipe recipe) {
307 return new DiscriminatorDispatcherNode(reteContainer, recipe.getDiscriminationColumnIndex());
308 }
309
310 private Supplier instantiateNode(ReteContainer reteContainer, TrimmerRecipe recipe) {
311 return new TrimmerNode(reteContainer, toMask(recipe.getMask()));
312 }
313
314 private Supplier instantiateNode(ReteContainer reteContainer, InequalityFilterRecipe recipe) {
315 Tunnel result = new InequalityFilterNode(reteContainer, recipe.getSubject(),
316 TupleMask.fromSelectedIndices(recipe.getParent().getArity(), recipe.getInequals()));
317 return result;
318 }
319
320 private Supplier instantiateNode(ReteContainer reteContainer, EqualityFilterRecipe recipe) {
321 final int[] equalIndices = TupleMask.integersToIntArray(recipe.getIndices());
322 return new EqualityFilterNode(reteContainer, equalIndices);
323 }
324
325 private Supplier instantiateNode(ReteContainer reteContainer, AntiJoinRecipe recipe) {
326 return new ExistenceNode(reteContainer, true);
327 }
328
329 private Supplier instantiateNode(ReteContainer reteContainer, SemiJoinRecipe recipe) {
330 return new ExistenceNode(reteContainer, false);
331 }
332
333 private Supplier instantiateNode(ReteContainer reteContainer, JoinRecipe recipe) {
334 return new JoinNode(reteContainer, toMask(recipe.getRightParentComplementaryMask()));
335 }
336
337 // HELPERS
338
339 private IExpressionEvaluator toIExpressionEvaluator(ExpressionDefinition expressionDefinition) {
340 final Object evaluator = expressionDefinition.getEvaluator();
341 if (evaluator instanceof IExpressionEvaluator) {
342 return (IExpressionEvaluator) evaluator;
343 }
344 throw new IllegalArgumentException("No runtime support for expression evaluator: " + evaluator);
345 }
346
347 private IRelationEvaluator toIRelationEvaluator(ExpressionDefinition expressionDefinition) {
348 final Object evaluator = expressionDefinition.getEvaluator();
349 if (evaluator instanceof IRelationEvaluator) {
350 return (IRelationEvaluator) evaluator;
351 }
352 throw new IllegalArgumentException("No runtime support for relation evaluator: " + evaluator);
353 }
354
355 private Map<String, Integer> toStringIndexMap(final EMap<String, Integer> mappedIndices) {
356 final HashMap<String, Integer> result = new HashMap<String, Integer>();
357 for (java.util.Map.Entry<String, Integer> entry : mappedIndices) {
358 result.put(entry.getKey(), entry.getValue());
359 }
360 return result;
361 }
362
363 /** Mask can be null */
364 private TupleMask toMaskOrNull(Mask mask) {
365 if (mask == null)
366 return null;
367 else
368 return toMask(mask);
369 }
370
371 /** Mask is non-null. */
372 private TupleMask toMask(Mask mask) {
373 return TupleMask.fromSelectedIndices(mask.getSourceArity(), mask.getSourceIndices());
374 }
375
376}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.network;
11
12import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
16import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
17import tools.refinery.viatra.runtime.rete.boundary.InputConnector;
18import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper;
19import tools.refinery.viatra.runtime.rete.index.Indexer;
20import tools.refinery.viatra.runtime.rete.index.OnetimeIndexer;
21import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
22import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand;
23import tools.refinery.viatra.runtime.rete.recipes.*;
24import tools.refinery.viatra.runtime.rete.recipes.helper.RecipeRecognizer;
25import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper;
26import tools.refinery.viatra.runtime.rete.remote.Address;
27import tools.refinery.viatra.runtime.rete.remote.RemoteReceiver;
28import tools.refinery.viatra.runtime.rete.remote.RemoteSupplier;
29import tools.refinery.viatra.runtime.rete.traceability.ActiveNodeConflictTrace;
30import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo;
31import tools.refinery.viatra.runtime.rete.traceability.UserRequestTrace;
32import tools.refinery.viatra.runtime.rete.util.Options;
33
34import java.util.Map;
35import java.util.Set;
36
37/**
38 * Stores the internal parts of a rete network. Nodes are stored according to type and parameters.
39 *
40 * @author Gabor Bergmann
41 */
42public class NodeProvisioner {
43
44 // boolean activeStorage = true;
45
46 ReteContainer reteContainer;
47 NodeFactory nodeFactory;
48 ConnectionFactory connectionFactory;
49 InputConnector inputConnector;
50 IQueryRuntimeContext runtimeContext;
51
52 // TODO as recipe?
53 Map<Supplier, RemoteReceiver> remoteReceivers = CollectionsFactory.createMap();
54 Map<Address<? extends Supplier>, RemoteSupplier> remoteSuppliers = CollectionsFactory.createMap();
55
56 private RecipeRecognizer recognizer;
57
58 /**
59 * PRE: NodeFactory, ConnectionFactory must exist
60 *
61 * @param reteContainer
62 * the ReteNet whose interior is to be mapped.
63 */
64 public NodeProvisioner(ReteContainer reteContainer) {
65 super();
66 this.reteContainer = reteContainer;
67 this.nodeFactory = reteContainer.getNodeFactory();
68 this.connectionFactory = reteContainer.getConnectionFactory();
69 this.inputConnector = reteContainer.getInputConnectionFactory();
70 runtimeContext = reteContainer.getNetwork().getEngine().getRuntimeContext();
71 recognizer = new RecipeRecognizer(runtimeContext);
72 }
73
74 public synchronized Address<? extends Node> getOrCreateNodeByRecipe(RecipeTraceInfo recipeTrace) {
75 ReteNodeRecipe recipe = recipeTrace.getRecipe();
76 Address<? extends Node> result = getNodesByRecipe().get(recipe);
77 if (result != null) {
78 // NODE ALREADY CONSTRUCTED FOR RECIPE, only needs to add trace
79 if (getRecipeTraces().add(recipeTrace))
80 result.getNodeCache().assignTraceInfo(recipeTrace);
81 } else {
82 // No node for this recipe object - but equivalent recipes still
83 // reusable
84 ReteNodeRecipe canonicalRecipe = recognizer.canonicalizeRecipe(recipe);
85 if (canonicalRecipe != recipe) {
86 // FOUND EQUIVALENT RECIPE
87 result = getNodesByRecipe().get(canonicalRecipe);
88 if (result != null) {
89 // NODE ALREADY CONSTRUCTED FOR EQUIVALENT RECIPE
90 recipeTrace.shadowWithEquivalentRecipe(canonicalRecipe);
91 getNodesByRecipe().put(recipe, result);
92 if (getRecipeTraces().add(recipeTrace))
93 result.getNodeCache().assignTraceInfo(recipeTrace);
94 // Bug 491922: ensure that recipe shadowing propagates to
95 // parent traces
96 // note that if equivalentRecipes() becomes more
97 // sophisticated
98 // and considers recipes with different parents, this might
99 // have to be changed
100 ensureParents(recipeTrace);
101 } else {
102 // CONSTRUCTION IN PROGRESS FOR EQUIVALENT RECIPE
103 if (recipe instanceof IndexerRecipe) {
104 // this is allowed for indexers;
105 // go on with the construction, as the same indexer node
106 // will be obtained anyways
107 } else {
108 throw new IllegalStateException(
109 "This should not happen: " + "non-indexer nodes are are supposed to be constructed "
110 + "as soon as they are designated as canonical recipes");
111 }
112 }
113 }
114 if (result == null) {
115 // MUST INSTANTIATE NEW NODE FOR RECIPE
116 final Node freshNode = instantiateNodeForRecipe(recipeTrace, recipe);
117 result = reteContainer.makeAddress(freshNode);
118 }
119 }
120 return result;
121 }
122
123 private Set<RecipeTraceInfo> getRecipeTraces() {
124 return reteContainer.network.recipeTraces;
125 }
126
127 private Node instantiateNodeForRecipe(RecipeTraceInfo recipeTrace, final ReteNodeRecipe recipe) {
128 this.getRecipeTraces().add(recipeTrace);
129 if (recipe instanceof IndexerRecipe) {
130
131 // INSTANTIATE AND HOOK UP
132 // (cannot delay hooking up, because parent determines indexer
133 // implementation)
134 ensureParents(recipeTrace);
135 final ReteNodeRecipe parentRecipe = recipeTrace.getParentRecipeTraces().iterator().next().getRecipe();
136 final Indexer result = nodeFactory.createIndexer(reteContainer, (IndexerRecipe) recipe,
137 asSupplier(
138 (Address<? extends Supplier>) reteContainer.network.getExistingNodeByRecipe(parentRecipe)),
139 recipeTrace);
140
141 // REMEMBER
142 if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) {
143 getNodesByRecipe().put(recipe, reteContainer.makeAddress(result));
144 }
145
146 return result;
147 } else {
148
149 // INSTANTIATE
150 Node result = nodeFactory.createNode(reteContainer, recipe, recipeTrace);
151
152 // REMEMBER
153 if (Options.nodeSharingOption == Options.NodeSharingOption.ALL) {
154 getNodesByRecipe().put(recipe, reteContainer.makeAddress(result));
155 }
156
157 // HOOK UP
158 // (recursion-tolerant due to this delayed order of initialization)
159 if (recipe instanceof InputRecipe) {
160 inputConnector.connectInput((InputRecipe) recipe, result);
161 } else {
162 if (recipe instanceof InputFilterRecipe)
163 inputConnector.connectInputFilter((InputFilterRecipe) recipe, result);
164 ensureParents(recipeTrace);
165 connectionFactory.connectToParents(recipeTrace, result);
166 }
167 return result;
168 }
169 }
170
171 private Map<ReteNodeRecipe, Address<? extends Node>> getNodesByRecipe() {
172 return reteContainer.network.nodesByRecipe;
173 }
174
175 private void ensureParents(RecipeTraceInfo recipeTrace) {
176 for (RecipeTraceInfo parentTrace : recipeTrace.getParentRecipeTraces()) {
177 getOrCreateNodeByRecipe(parentTrace);
178 }
179 }
180
181 //// Remoting - TODO eliminate?
182
183 synchronized RemoteReceiver accessRemoteReceiver(Address<? extends Supplier> address) {
184 throw new UnsupportedOperationException("Multi-container Rete not supported yet");
185 // if (!reteContainer.isLocal(address))
186 // return
187 // address.getContainer().getProvisioner().accessRemoteReceiver(address);
188 // Supplier localSupplier = reteContainer.resolveLocal(address);
189 // RemoteReceiver result = remoteReceivers.get(localSupplier);
190 // if (result == null) {
191 // result = new RemoteReceiver(reteContainer);
192 // reteContainer.connect(localSupplier, result); // stateless node, no
193 // // synch required
194 //
195 // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER)
196 // remoteReceivers.put(localSupplier, result);
197 // }
198 // return result;
199 }
200
201 /**
202 * @pre: address is NOT local
203 */
204 synchronized RemoteSupplier accessRemoteSupplier(Address<? extends Supplier> address) {
205 throw new UnsupportedOperationException("Multi-container Rete not supported yet");
206 // RemoteSupplier result = remoteSuppliers.get(address);
207 // if (result == null) {
208 // result = new RemoteSupplier(reteContainer,
209 // address.getContainer().getProvisioner()
210 // .accessRemoteReceiver(address));
211 // // network.connectAndSynchronize(supplier, result);
212 //
213 // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER)
214 // remoteSuppliers.put(address, result);
215 // }
216 // return result;
217 }
218
219 /**
220 * The powerful method for accessing any (supplier) Address as a local supplier.
221 */
222 public Supplier asSupplier(Address<? extends Supplier> address) {
223 if (!reteContainer.isLocal(address))
224 return accessRemoteSupplier(address);
225 else
226 return reteContainer.resolveLocal(address);
227 }
228
229 /** the composite key tuple is formed as (RecipeTraceInfo, TupleMask) */
230 private Map<Tuple, UserRequestTrace> projectionIndexerUserRequests = CollectionsFactory.createMap();
231
232 // local version
233 // TODO remove?
234 public synchronized ProjectionIndexer accessProjectionIndexer(RecipeTraceInfo productionTrace, TupleMask mask) {
235 Tuple tableKey = Tuples.staticArityFlatTupleOf(productionTrace, mask);
236 UserRequestTrace indexerTrace = projectionIndexerUserRequests.computeIfAbsent(tableKey, k -> {
237 final ProjectionIndexerRecipe projectionIndexerRecipe = projectionIndexerRecipe(
238 productionTrace, mask);
239 return new UserRequestTrace(projectionIndexerRecipe, productionTrace);
240 });
241 final Address<? extends Node> address = getOrCreateNodeByRecipe(indexerTrace);
242 return (ProjectionIndexer) reteContainer.resolveLocal(address);
243 }
244
245 // local version
246 public synchronized ProjectionIndexer accessProjectionIndexerOnetime(RecipeTraceInfo supplierTrace,
247 TupleMask mask) {
248 if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER)
249 return accessProjectionIndexer(supplierTrace, mask);
250
251 final Address<? extends Node> supplierAddress = getOrCreateNodeByRecipe(supplierTrace);
252 Supplier supplier = (Supplier) reteContainer.resolveLocal(supplierAddress);
253
254 OnetimeIndexer result = new OnetimeIndexer(reteContainer, mask);
255 reteContainer.getDelayedCommandQueue().add(new DelayedConnectCommand(supplier, result, reteContainer));
256
257 return result;
258 }
259
260 // local, read-only version
261 public synchronized ProjectionIndexer peekProjectionIndexer(RecipeTraceInfo supplierTrace, TupleMask mask) {
262 final Address<? extends Node> address = getNodesByRecipe().get(projectionIndexerRecipe(supplierTrace, mask));
263 return address == null ? null : (ProjectionIndexer) reteContainer.resolveLocal(address);
264 }
265
266 private ProjectionIndexerRecipe projectionIndexerRecipe(
267 RecipeTraceInfo parentTrace, TupleMask mask) {
268 final ReteNodeRecipe parentRecipe = parentTrace.getRecipe();
269 Tuple tableKey = Tuples.staticArityFlatTupleOf(parentRecipe, mask);
270 ProjectionIndexerRecipe projectionIndexerRecipe = resultSeedRecipes.computeIfAbsent(tableKey, k ->
271 RecipesHelper.projectionIndexerRecipe(parentRecipe, CompilerHelper.toRecipeMask(mask))
272 );
273 return projectionIndexerRecipe;
274 }
275
276 /** the composite key tuple is formed as (ReteNodeRecipe, TupleMask) */
277 private Map<Tuple, ProjectionIndexerRecipe> resultSeedRecipes = CollectionsFactory.createMap();
278
279 // public synchronized Address<? extends Supplier>
280 // accessValueBinderFilterNode(
281 // Address<? extends Supplier> supplierAddress, int bindingIndex, Object
282 // bindingValue) {
283 // Supplier supplier = asSupplier(supplierAddress);
284 // Object[] paramsArray = { supplier.getNodeId(), bindingIndex, bindingValue
285 // };
286 // Tuple params = new FlatTuple(paramsArray);
287 // ValueBinderFilterNode result = valueBinderFilters.get(params);
288 // if (result == null) {
289 // result = new ValueBinderFilterNode(reteContainer, bindingIndex,
290 // bindingValue);
291 // reteContainer.connect(supplier, result); // stateless node, no synch
292 // // required
293 //
294 // if (Options.nodeSharingOption == Options.NodeSharingOption.ALL)
295 // valueBinderFilters.put(params, result);
296 // }
297 // return reteContainer.makeAddress(result);
298 // }
299
300 /**
301 * Returns a copy of the given indexer that is an active node by itself (created if does not exist). (Convention:
302 * attached with same mask to a transparent node that is attached to parent node.) Node is created if it does not
303 * exist yet.
304 *
305 * @return an identical but active indexer
306 */
307 // TODO rethink traceability
308 RecipeTraceInfo accessActiveIndexer(RecipeTraceInfo inactiveIndexerRecipeTrace) {
309 final RecipeTraceInfo parentRecipeTrace = inactiveIndexerRecipeTrace.getParentRecipeTraces().iterator().next();
310 final ProjectionIndexerRecipe inactiveIndexerRecipe = (ProjectionIndexerRecipe) inactiveIndexerRecipeTrace
311 .getRecipe();
312
313 final TransparentRecipe transparentRecipe = RecipesFactory.eINSTANCE.createTransparentRecipe();
314 transparentRecipe.setParent(parentRecipeTrace.getRecipe());
315 final ActiveNodeConflictTrace transparentRecipeTrace = new ActiveNodeConflictTrace(transparentRecipe,
316 parentRecipeTrace, inactiveIndexerRecipeTrace);
317
318 final ProjectionIndexerRecipe activeIndexerRecipe = RecipesFactory.eINSTANCE
319 .createProjectionIndexerRecipe();
320 activeIndexerRecipe.setParent(transparentRecipe);
321 activeIndexerRecipe.setMask(inactiveIndexerRecipe.getMask());
322 final ActiveNodeConflictTrace activeIndexerRecipeTrace = new ActiveNodeConflictTrace(activeIndexerRecipe,
323 transparentRecipeTrace, inactiveIndexerRecipeTrace);
324
325 return activeIndexerRecipeTrace;
326 }
327
328 // /**
329 // * @param parent
330 // * @return
331 // */
332 // private TransparentNode accessTransparentNodeInternal(Supplier parent) {
333 // nodeFactory.
334 // return null;
335 // }
336
337 // public synchronized void registerSpecializedProjectionIndexer(Node node,
338 // ProjectionIndexer indexer) {
339 // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) {
340 // Object[] paramsArray = { node.getNodeId(), indexer.getMask() };
341 // Tuple params = new FlatTuple(paramsArray);
342 // projectionIndexers.put(params, indexer);
343 // }
344 // }
345
346}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network;
10
11import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
14import tools.refinery.viatra.runtime.matchers.util.Direction;
15
16/**
17 * @author Tamas Szabo
18 * @since 2.0
19 */
20public interface PosetAwareReceiver extends Receiver {
21
22 public TupleMask getCoreMask();
23
24 public TupleMask getPosetMask();
25
26 public IPosetComparator getPosetComparator();
27
28 /**
29 * Updates the receiver with a newly found or lost partial matching also providing information
30 * whether the update is a monotone change or not.
31 *
32 * @param direction the direction of the update
33 * @param update the update tuple
34 * @param monotone true if the update is monotone, false otherwise
35 * @since 2.4
36 */
37 public void updateWithPosetInfo(Direction direction, Tuple update, boolean monotone);
38
39}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.network;
11
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15
16/**
17 * Interface intended for nodes containing complete matches.
18 *
19 * @author Gabor Bergmann
20 */
21public interface ProductionNode extends Tunnel, Iterable<Tuple> {
22
23 /**
24 * @return the position mapping of this particular pattern that maps members of the tuple type to their positions
25 */
26 Map<String, Integer> getPosMapping();
27
28}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.network;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Map.Entry;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
19import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
20
21/**
22 * ALL METHODS: FOR INTERNAL USE ONLY; ONLY INVOKE FROM {@link ReteContainer}
23 *
24 * @author Gabor Bergmann
25 * @noimplement This interface is not intended to be implemented by external clients.
26 */
27public interface Receiver extends Node {
28
29 /**
30 * Updates the receiver with a newly found or lost partial matching.
31 *
32 * @since 2.4
33 */
34 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp);
35
36 /**
37 * Updates the receiver in batch style with a collection of updates. The input collection consists of pairs in the
38 * form (t, c) where t is an update tuple and c is the count. The count can also be negative, and it specifies how
39 * many times the tuple t gets deleted or inserted. The default implementation of this method simply calls
40 * {@link #update(Direction, Tuple, Timestamp)} individually for all updates.
41 *
42 * @since 2.8
43 */
44 public default void batchUpdate(final Collection<Map.Entry<Tuple, Integer>> updates, final Timestamp timestamp) {
45 for (final Entry<Tuple, Integer> entry : updates) {
46 int count = entry.getValue();
47
48 Direction direction;
49 if (count < 0) {
50 direction = Direction.DELETE;
51 count = -count;
52 } else {
53 direction = Direction.INSERT;
54 }
55
56 for (int i = 0; i < count; i++) {
57 update(direction, entry.getKey(), timestamp);
58 }
59 }
60 }
61
62 /**
63 * Returns the {@link Mailbox} of this receiver.
64 *
65 * @return the mailbox
66 * @since 2.0
67 */
68 public Mailbox getMailbox();
69
70 /**
71 * appends a parent that will continuously send insert and revoke updates to this supplier
72 */
73 void appendParent(final Supplier supplier);
74
75 /**
76 * removes a parent
77 */
78 void removeParent(final Supplier supplier);
79
80 /**
81 * access active parent
82 */
83 Collection<Supplier> getParents();
84
85}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network;
10
11/**
12 * A rederivable node can potentially re-derive tuples after the Rete network has finished the delivery of messages.
13 *
14 * @author Tamas Szabo
15 * @since 1.6
16 */
17public interface RederivableNode extends Node, IGroupable {
18
19 /**
20 * The method is called by the {@link ReteContainer} to re-derive tuples after the normal messages have been
21 * delivered and consumed. The re-derivation process may trigger the creation and delivery of further messages
22 * and further re-derivation rounds.
23 */
24 public void rederiveOne();
25
26 /**
27 * Returns true if this node actually runs in DRed mode (not necessarily).
28 *
29 * @return true if the node is operating in DRed mode
30 * @since 2.0
31 */
32 public boolean isInDRedMode();
33
34}
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 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.viatra.runtime.rete.network;
7
8import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
9
10import java.util.Collection;
11
12public interface ReinitializedNode {
13 void reinitializeWith(Collection<Tuple> tuples);
14}
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..79e0526d
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java
@@ -0,0 +1,729 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10
11package tools.refinery.viatra.runtime.rete.network;
12
13import org.apache.log4j.Logger;
14import tools.refinery.viatra.runtime.CancellationToken;
15import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.Clearable;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
21import tools.refinery.viatra.runtime.rete.boundary.InputConnector;
22import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
23import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
24import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.communication.timeless.TimelessCommunicationTracker;
27import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyCommunicationTracker;
28import tools.refinery.viatra.runtime.rete.network.delayed.DelayedCommand;
29import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand;
30import tools.refinery.viatra.runtime.rete.network.delayed.DelayedDisconnectCommand;
31import tools.refinery.viatra.runtime.rete.remote.Address;
32import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
33import tools.refinery.viatra.runtime.rete.single.TrimmerNode;
34import tools.refinery.viatra.runtime.rete.util.Options;
35
36import java.util.*;
37import java.util.function.Function;
38
39/**
40 * @author Gabor Bergmann
41 *
42 * Mutexes: externalMessageLock - enlisting messages into and retrieving from the external message queue
43 * @since 2.2
44 */
45public final class ReteContainer {
46
47 protected Thread consumerThread = null;
48 protected boolean killed = false;
49
50 protected Network network;
51
52 protected LinkedList<Clearable> clearables;
53 protected Map<Long, Node> nodesById;
54 protected long nextId = 0;
55
56 protected ConnectionFactory connectionFactory;
57 protected NodeProvisioner nodeProvisioner;
58
59 protected Deque<UpdateMessage> internalMessageQueue = new ArrayDeque<UpdateMessage>();
60 protected/* volatile */Deque<UpdateMessage> externalMessageQueue = new ArrayDeque<UpdateMessage>();
61 protected Object externalMessageLock = new Object();
62 protected Long clock = 1L; // even: steady state, odd: active queue; access
63 // ONLY with messageQueue locked!
64 protected Map<ReteContainer, Long> terminationCriteria = null;
65 protected final Logger logger;
66 protected final CommunicationTracker tracker;
67
68 protected final IQueryBackendContext backendContext;
69
70 protected Set<DelayedCommand> delayedCommandQueue;
71 protected Set<DelayedCommand> delayedCommandBuffer;
72 protected boolean executingDelayedCommands;
73
74 protected final TimelyConfiguration timelyConfiguration;
75
76 private final CancellationToken cancellationToken;
77
78 /**
79 * @param threaded
80 * false if operating in a single-threaded environment
81 */
82 public ReteContainer(Network network, boolean threaded) {
83 super();
84 this.network = network;
85 this.backendContext = network.getEngine().getBackendContext();
86 this.timelyConfiguration = network.getEngine().getTimelyConfiguration();
87 cancellationToken = backendContext.getRuntimeContext().getCancellationToken();
88
89 this.delayedCommandQueue = new LinkedHashSet<DelayedCommand>();
90 this.delayedCommandBuffer = new LinkedHashSet<DelayedCommand>();
91 this.executingDelayedCommands = false;
92
93 if (this.isTimelyEvaluation()) {
94 this.tracker = new TimelyCommunicationTracker(this.getTimelyConfiguration());
95 } else {
96 this.tracker = new TimelessCommunicationTracker();
97 }
98
99 this.nodesById = CollectionsFactory.createMap();
100 this.clearables = new LinkedList<Clearable>();
101 this.logger = network.getEngine().getLogger();
102
103 this.connectionFactory = new ConnectionFactory(this);
104 this.nodeProvisioner = new NodeProvisioner(this);
105
106 if (threaded) {
107 this.terminationCriteria = CollectionsFactory.createMap();
108 this.consumerThread = new Thread("Rete thread of " + ReteContainer.super.toString()) {
109 @Override
110 public void run() {
111 messageConsumptionCycle();
112 }
113 };
114 this.consumerThread.start();
115 }
116 }
117
118 /**
119 * @since 2.4
120 */
121 public boolean isTimelyEvaluation() {
122 return this.timelyConfiguration != null;
123 }
124
125 /**
126 * @since 2.4
127 */
128 public TimelyConfiguration getTimelyConfiguration() {
129 return this.timelyConfiguration;
130 }
131
132 /**
133 * @since 1.6
134 * @return the communication graph of the nodes, incl. message scheduling
135 */
136 public CommunicationTracker getCommunicationTracker() {
137 return tracker;
138 }
139
140 /**
141 * Stops this container. To be called by Network.kill()
142 */
143 public void kill() {
144 killed = true;
145 if (consumerThread != null)
146 consumerThread.interrupt();
147 }
148
149 /**
150 * Establishes connection between a supplier and a receiver node, regardless which container they are in. Assumption
151 * is that this container is the home of the receiver, but it is not strictly necessary.
152 *
153 * @param synchronise
154 * indicates whether the receiver should be synchronised to the current contents of the supplier
155 */
156 public void connectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver,
157 boolean synchronise) {
158 if (!isLocal(receiver))
159 receiver.getContainer().connectRemoteNodes(supplier, receiver, synchronise);
160 else {
161 Receiver child = resolveLocal(receiver);
162 connectRemoteSupplier(supplier, child, synchronise);
163 }
164 }
165
166 /**
167 * Severs connection between a supplier and a receiver node, regardless which container they are in. Assumption is
168 * that this container is the home of the receiver, but it is not strictly necessary.
169 *
170 * @param desynchronise
171 * indicates whether the current contents of the supplier should be subtracted from the receiver
172 */
173 public void disconnectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver,
174 boolean desynchronise) {
175 if (!isLocal(receiver))
176 receiver.getContainer().disconnectRemoteNodes(supplier, receiver, desynchronise);
177 else {
178 Receiver child = resolveLocal(receiver);
179 disconnectRemoteSupplier(supplier, child, desynchronise);
180 }
181 }
182
183 /**
184 * Establishes connection between a remote supplier and a local receiver node.
185 *
186 * @param synchronise
187 * indicates whether the receiver should be synchronised to the current contents of the supplier
188 */
189 public void connectRemoteSupplier(Address<? extends Supplier> supplier, Receiver receiver, boolean synchronise) {
190 Supplier parent = nodeProvisioner.asSupplier(supplier);
191 if (synchronise)
192 connectAndSynchronize(parent, receiver);
193 else
194 connect(parent, receiver);
195 }
196
197 /**
198 * Severs connection between a remote supplier and a local receiver node.
199 *
200 * @param desynchronise
201 * indicates whether the current contents of the supplier should be subtracted from the receiver
202 */
203 public void disconnectRemoteSupplier(Address<? extends Supplier> supplier, Receiver receiver,
204 boolean desynchronise) {
205 Supplier parent = nodeProvisioner.asSupplier(supplier);
206 if (desynchronise)
207 disconnectAndDesynchronize(parent, receiver);
208 else
209 disconnect(parent, receiver);
210 }
211
212 /**
213 * Connects a receiver to a supplier
214 */
215 public void connect(Supplier supplier, Receiver receiver) {
216 supplier.appendChild(receiver);
217 receiver.appendParent(supplier);
218 tracker.registerDependency(supplier, receiver);
219 }
220
221 /**
222 * Disconnects a receiver from a supplier
223 */
224 public void disconnect(Supplier supplier, Receiver receiver) {
225 supplier.removeChild(receiver);
226 receiver.removeParent(supplier);
227 tracker.unregisterDependency(supplier, receiver);
228 }
229
230 /**
231 * @since 2.3
232 */
233 public boolean isExecutingDelayedCommands() {
234 return this.executingDelayedCommands;
235 }
236
237 /**
238 * @since 2.3
239 */
240 public Set<DelayedCommand> getDelayedCommandQueue() {
241 if (this.executingDelayedCommands) {
242 return this.delayedCommandBuffer;
243 } else {
244 return this.delayedCommandQueue;
245 }
246 }
247
248 /**
249 * Connects a receiver to a remote supplier, and synchronizes it to the current contents of the supplier
250 */
251 public void connectAndSynchronize(Supplier supplier, Receiver receiver) {
252 supplier.appendChild(receiver);
253 receiver.appendParent(supplier);
254 tracker.registerDependency(supplier, receiver);
255 getDelayedCommandQueue().add(new DelayedConnectCommand(supplier, receiver, this));
256 }
257
258 /**
259 * Disconnects a receiver from a supplier
260 */
261 public void disconnectAndDesynchronize(Supplier supplier, Receiver receiver) {
262 final boolean wasInSameSCC = this.isTimelyEvaluation() && this.tracker.areInSameGroup(supplier, receiver);
263 supplier.removeChild(receiver);
264 receiver.removeParent(supplier);
265 tracker.unregisterDependency(supplier, receiver);
266 getDelayedCommandQueue().add(new DelayedDisconnectCommand(supplier, receiver, this, wasInSameSCC));
267 }
268
269 /**
270 * @since 2.3
271 */
272 public void executeDelayedCommands() {
273 if (!this.delayedCommandQueue.isEmpty()) {
274 flushUpdates();
275 this.executingDelayedCommands = true;
276 for (final DelayedCommand command : this.delayedCommandQueue) {
277 command.run();
278 }
279 this.delayedCommandQueue = this.delayedCommandBuffer;
280 this.delayedCommandBuffer = new LinkedHashSet<DelayedCommand>();
281 flushUpdates();
282 this.executingDelayedCommands = false;
283 }
284 }
285
286 /**
287 * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The receiver is
288 * indicated by the Address. Designed to be called by the Network, DO NOT use in any other way. @pre:
289 * address.container == this, e.g. address MUST be local
290 *
291 * @return the value of the container's clock at the time when the message was accepted into the local message queue
292 */
293 long sendUpdateToLocalAddress(Address<? extends Receiver> address, Direction direction, Tuple updateElement) {
294 long timestamp;
295 Receiver receiver = resolveLocal(address);
296 UpdateMessage message = new UpdateMessage(receiver, direction, updateElement);
297 synchronized (externalMessageLock) {
298 externalMessageQueue.add(message);
299 timestamp = clock;
300 externalMessageLock.notifyAll();
301 }
302
303 return timestamp;
304
305 }
306
307 /**
308 * Sends multiple update messages atomically to the receiver node, indicating a newly found or lost partial
309 * matching. The receiver is indicated by the Address. Designed to be called by the Network, DO NOT use in any other
310 * way. @pre: address.container == this, e.g. address MUST be local @pre: updateElements is nonempty!
311 *
312 * @return the value of the container's clock at the time when the message was accepted into the local message queue
313 */
314 long sendUpdatesToLocalAddress(Address<? extends Receiver> address, Direction direction,
315 Collection<Tuple> updateElements) {
316
317 long timestamp;
318 Receiver receiver = resolveLocal(address);
319 // UpdateMessage message = new UpdateMessage(receiver, direction,
320 // updateElement);
321 synchronized (externalMessageLock) {
322 for (Tuple ps : updateElements)
323 externalMessageQueue.add(new UpdateMessage(receiver, direction, ps));
324 // messageQueue.add(new UpdateMessage(resolveLocal(address),
325 // direction, updateElement));
326 // this.sendUpdateInternal(resolveLocal(address), direction,
327 // updateElement);
328 timestamp = clock;
329 externalMessageLock.notifyAll();
330 }
331
332 return timestamp;
333 }
334
335 /**
336 * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The receiver is
337 * indicated by the Address. Designed to be called by the Network in single-threaded operation, DO NOT use in any
338 * other way.
339 */
340 void sendUpdateToLocalAddressSingleThreaded(Address<? extends Receiver> address, Direction direction,
341 Tuple updateElement) {
342 Receiver receiver = resolveLocal(address);
343 UpdateMessage message = new UpdateMessage(receiver, direction, updateElement);
344 internalMessageQueue.add(message);
345 }
346
347 /**
348 * Sends multiple update messages to the receiver node, indicating a newly found or lost partial matching. The
349 * receiver is indicated by the Address. Designed to be called by the Network in single-threaded operation, DO NOT
350 * use in any other way.
351 *
352 * @pre: address.container == this, e.g. address MUST be local
353 */
354 void sendUpdatesToLocalAddressSingleThreaded(Address<? extends Receiver> address, Direction direction,
355 Collection<Tuple> updateElements) {
356 Receiver receiver = resolveLocal(address);
357 for (Tuple ps : updateElements)
358 internalMessageQueue.add(new UpdateMessage(receiver, direction, ps));
359 }
360
361 /**
362 * Sends an update message to a node in a different container. The receiver is indicated by the Address. Designed to
363 * be called by RemoteReceivers, DO NOT use in any other way.
364 *
365 * @since 2.4
366 */
367 public void sendUpdateToRemoteAddress(Address<? extends Receiver> address, Direction direction,
368 Tuple updateElement) {
369 ReteContainer otherContainer = address.getContainer();
370 long otherClock = otherContainer.sendUpdateToLocalAddress(address, direction, updateElement);
371 // Long criterion = terminationCriteria.get(otherContainer);
372 // if (criterion==null || otherClock > criterion)
373 terminationCriteria.put(otherContainer, otherClock);
374 }
375
376 /**
377 * Finalises all update sequences and returns. To be called from user threads (e.g. network construction).
378 */
379 public void flushUpdates() {
380 network.waitForReteTermination();
381 // synchronized (messageQueue)
382 // {
383 // while (!messageQueue.isEmpty())
384 // {
385 // try {
386 // UpdateMessage message = messageQueue.take();
387 // message.receiver.update(message.direction, message.updateElement);
388 // } catch (InterruptedException e) {}
389 // }
390 // }
391 }
392
393 /**
394 * Retrieves a safe copy of the contents of a supplier.
395 *
396 * <p> Note that there may be multiple copies of a Tuple in case of a {@link TrimmerNode}, so the result is not always a set.
397 *
398 * @param flush if true, a flush is performed before pulling the contents
399 * @since 2.3
400 */
401 public Collection<Tuple> pullContents(final Supplier supplier, final boolean flush) {
402 if (flush) {
403 flushUpdates();
404 }
405 final Collection<Tuple> collector = new ArrayList<Tuple>();
406 supplier.pullInto(collector, flush);
407 return collector;
408 }
409
410 /**
411 * @since 2.4
412 */
413 public Map<Tuple, Timeline<Timestamp>> pullContentsWithTimeline(final Supplier supplier, final boolean flush) {
414 if (flush) {
415 flushUpdates();
416 }
417 final Map<Tuple, Timeline<Timestamp>> collector = CollectionsFactory.createMap();
418 supplier.pullIntoWithTimeline(collector, flush);
419 return collector;
420 }
421
422 /**
423 * Retrieves the contents of a SingleInputNode's parentage.
424 *
425 * @since 2.3
426 */
427 public Collection<Tuple> pullPropagatedContents(final SingleInputNode supplier, final boolean flush) {
428 if (flush) {
429 flushUpdates();
430 }
431 final Collection<Tuple> collector = new LinkedList<Tuple>();
432 supplier.propagatePullInto(collector, flush);
433 return collector;
434 }
435
436 /**
437 * Retrieves the timestamp-aware contents of a SingleInputNode's parentage.
438 *
439 * @since 2.3
440 */
441 public Map<Tuple, Timeline<Timestamp>> pullPropagatedContentsWithTimestamp(final SingleInputNode supplier,
442 final boolean flush) {
443 if (flush) {
444 flushUpdates();
445 }
446 final Map<Tuple, Timeline<Timestamp>> collector = CollectionsFactory.createMap();
447 supplier.propagatePullIntoWithTimestamp(collector, flush);
448 return collector;
449 }
450
451 /**
452 * Retrieves the contents of a supplier for a remote caller. Assumption is that this container is the home of the
453 * supplier, but it is not strictly necessary.
454 *
455 * @param supplier
456 * the address of the supplier to be pulled.
457 * @since 2.3
458 */
459 public Collection<Tuple> remotePull(Address<? extends Supplier> supplier, boolean flush) {
460 if (!isLocal(supplier))
461 return supplier.getContainer().remotePull(supplier, flush);
462 return pullContents(resolveLocal(supplier), flush);
463 }
464
465 /**
466 * Proxies for the getPosMapping() of Production nodes. Retrieves the posmapping of a remote or local Production to
467 * a remote or local caller.
468 */
469 public Map<String, Integer> remotePosMapping(Address<? extends ProductionNode> production) {
470 if (!isLocal(production))
471 return production.getContainer().remotePosMapping(production);
472 return resolveLocal(production).getPosMapping();
473 }
474
475 /**
476 * Continually consumes update messages. Should be run on a dedicated thread.
477 */
478 void messageConsumptionCycle() {
479 while (!killed) // deliver messages on and on and on....
480 {
481 long incrementedClock = 0;
482 UpdateMessage message = null;
483
484 if (!internalMessageQueue.isEmpty()) // take internal messages first
485 message = internalMessageQueue.removeFirst();
486 else
487 // no internal message, take an incoming message
488 synchronized (externalMessageLock) { // no sleeping allowed,
489 // because external
490 // queue is locked for
491 // precise clocking of
492 // termination point!
493 if (!externalMessageQueue.isEmpty()) { // if external queue
494 // is non-empty,
495 // retrieve the next
496 // message instantly
497 message = takeExternalMessage();
498 } else { // if external queue is found empty (and this is
499 // the first time in a row)
500 incrementedClock = ++clock; // local termination point
501 // synchronized(clock){incrementedClock = ++clock;}
502 }
503 }
504
505 if (message == null) // both queues were empty
506 {
507 localUpdateTermination(incrementedClock); // report local
508 // termination point
509 while (message == null) // wait for a message while external
510 // queue is still empty
511 {
512 synchronized (externalMessageLock) {
513 while (externalMessageQueue.isEmpty()) {
514 try {
515 externalMessageLock.wait();
516 } catch (InterruptedException e) {
517 if (killed)
518 return;
519 }
520 }
521 message = takeExternalMessage();
522 }
523
524 }
525 }
526
527 // now we have a message to deliver
528 // NOTE: this method is not compatible with differential dataflow
529 message.receiver.update(message.direction, message.updateElement, Timestamp.ZERO);
530 }
531 }
532
533 /**
534 * @since 1.6
535 */
536 public static final Function<Node, String> NAME_MAPPER = input -> input.toString().substring(0,
537 Math.min(30, input.toString().length()));
538
539 /**
540 * Sends out all pending messages to their receivers. The delivery is governed by the communication tracker.
541 *
542 * @since 1.6
543 */
544 public void deliverMessagesSingleThreaded() {
545 if (!backendContext.areUpdatesDelayed()) {
546 if (Options.MONITOR_VIOLATION_OF_RETE_NODEGROUP_TOPOLOGICAL_SORTING) {
547 // known unreachable; enable for debugging only
548
549 CommunicationGroup lastGroup = null;
550 Set<CommunicationGroup> seenInThisCycle = new HashSet<>();
551
552 while (!tracker.isEmpty()) {
553 final CommunicationGroup group = tracker.getAndRemoveFirstGroup();
554
555 /**
556 * The current group does not violate the communication schema iff (1) it was not seen before OR (2)
557 * the last one that was seen is exactly the same as the current one this can happen if the group
558 * was added back because of in-group message passing
559 */
560 boolean okGroup = (group == lastGroup) || seenInThisCycle.add(group);
561
562 if (!okGroup) {
563 logger.error(
564 "[INTERNAL ERROR] Violation of communication schema! The communication component with representative "
565 + group.getRepresentative() + " has already been processed!");
566 }
567
568 group.deliverMessages();
569
570 lastGroup = group;
571 }
572
573 } else {
574 while (!tracker.isEmpty()) {
575 final CommunicationGroup group = tracker.getAndRemoveFirstGroup();
576 group.deliverMessages();
577 }
578 }
579 }
580 }
581
582 private void localUpdateTermination(long incrementedClock) {
583 network.reportLocalUpdateTermination(this, incrementedClock, terminationCriteria);
584 terminationCriteria.clear();
585
586 // synchronized(clock){++clock;} // +1 incrementing for parity and easy
587 // comparison
588 }
589
590 // @pre: externalMessageQueue synchronized && nonempty
591 private UpdateMessage takeExternalMessage() {
592 UpdateMessage message = externalMessageQueue.removeFirst();
593 if (!externalMessageQueue.isEmpty()) { // copy the whole queue over
594 // for speedup
595 Deque<UpdateMessage> temp = externalMessageQueue;
596 externalMessageQueue = internalMessageQueue;
597 internalMessageQueue = temp;
598 }
599 return message;
600 }
601
602 /**
603 * Provides an external address for the selected node.
604 *
605 * @pre node belongs to this container.
606 */
607 public <N extends Node> Address<N> makeAddress(N node) {
608 return new Address<N>(node);
609 }
610
611 /**
612 * Checks whether a certain address points to a node at this container.
613 */
614 public boolean isLocal(Address<? extends Node> address) {
615 return address.getContainer() == this;
616 }
617
618 /**
619 * Returns an addressed node at this container.
620 *
621 * @pre: address.container == this, e.g. address MUST be local
622 * @throws IllegalArgumentException
623 * if address is non-local
624 */
625 @SuppressWarnings("unchecked")
626 public <N extends Node> N resolveLocal(Address<N> address) {
627 if (this != address.getContainer())
628 throw new IllegalArgumentException(String.format("Address %s non-local at container %s", address, this));
629
630 N cached = address.getNodeCache();
631 if (cached != null)
632 return cached;
633 else {
634 N node = (N) nodesById.get(address.getNodeId());
635 address.setNodeCache(node);
636 return node;
637 }
638 }
639
640 /**
641 * Registers a node into the rete network (should be called by constructor). Every node MUST be registered by its
642 * constructor.
643 *
644 * @return the unique nodeId issued to the node.
645 */
646 public long registerNode(Node n) {
647 long id = nextId++;
648 nodesById.put(id, n);
649 return id;
650 }
651
652 /**
653 * Unregisters a node from the rete network. Do NOT call if node is still connected to other Nodes, or Adressed or
654 * otherwise referenced.
655 */
656 public void unregisterNode(Node n) {
657 nodesById.remove(n.getNodeId());
658 }
659
660 /**
661 * Registers a pattern memory into the rete network. Every memory MUST be registered by its owner node.
662 */
663 public void registerClearable(Clearable c) {
664 clearables.addFirst(c);
665 }
666
667 /**
668 * Unregisters a pattern memory from the rete network.
669 */
670 public void unregisterClearable(Clearable c) {
671 clearables.remove(c);
672 }
673
674 /**
675 * Clears all memory contents in the network. Reverts to initial state.
676 */
677 public void clearAll() {
678 for (Clearable c : clearables) {
679 c.clear();
680 }
681 }
682
683 public NodeFactory getNodeFactory() {
684 return network.getNodeFactory();
685 }
686
687 public ConnectionFactory getConnectionFactory() {
688 return connectionFactory;
689 }
690
691 public NodeProvisioner getProvisioner() {
692 return nodeProvisioner;
693 }
694
695 public Network getNetwork() {
696 return network;
697 }
698
699 @Override
700 public String toString() {
701 StringBuilder sb = new StringBuilder();
702 String separator = System.getProperty("line.separator");
703 sb.append(super.toString() + "[[[" + separator);
704 java.util.List<Long> keys = new java.util.ArrayList<Long>(nodesById.keySet());
705 java.util.Collections.sort(keys);
706 for (Long key : keys) {
707 sb.append(key + " -> " + nodesById.get(key) + separator);
708 }
709 sb.append("]]] of " + network);
710 return sb.toString();
711 }
712
713 /**
714 * Access all the Rete nodes inside this container.
715 *
716 * @return the collection of {@link Node} instances
717 */
718 public Collection<Node> getAllNodes() {
719 return nodesById.values();
720 }
721
722 public InputConnector getInputConnectionFactory() {
723 return network.getInputConnector();
724 }
725
726 public void checkCancelled() {
727 cancellationToken.checkCancelled();
728 }
729}
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..7dc7c4bc
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java
@@ -0,0 +1,123 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10
11package tools.refinery.viatra.runtime.rete.network;
12
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.rete.index.GenericProjectionIndexer;
18import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
21import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
22
23import java.util.Collection;
24import java.util.HashSet;
25import java.util.List;
26import java.util.Set;
27
28/**
29 * Base implementation for a supplier node.
30 *
31 * @author Gabor Bergmann
32 *
33 */
34public abstract class StandardNode extends BaseNode implements Supplier, NetworkStructureChangeSensitiveNode {
35 protected final List<Receiver> children = CollectionsFactory.createObserverList();
36 /**
37 * @since 2.2
38 */
39 protected final List<Mailbox> childMailboxes = CollectionsFactory.createObserverList();
40
41 public StandardNode(final ReteContainer reteContainer) {
42 super(reteContainer);
43 }
44
45 /**
46 * @since 2.4
47 */
48 protected void propagateUpdate(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
49 reteContainer.checkCancelled();
50 for (final Mailbox childMailbox : childMailboxes) {
51 childMailbox.postMessage(direction, updateElement, timestamp);
52 }
53 }
54
55 @Override
56 public void appendChild(final Receiver receiver) {
57 children.add(receiver);
58 childMailboxes.add(this.getCommunicationTracker().proxifyMailbox(this, receiver.getMailbox()));
59 }
60
61 @Override
62 public void removeChild(final Receiver receiver) {
63 children.remove(receiver);
64 Mailbox mailboxToRemove = null;
65 for (final Mailbox mailbox : childMailboxes) {
66 if (mailbox.getReceiver() == receiver) {
67 mailboxToRemove = mailbox;
68 break;
69 }
70 }
71 assert mailboxToRemove != null;
72 childMailboxes.remove(mailboxToRemove);
73 }
74
75 @Override
76 public void networkStructureChanged() {
77 childMailboxes.clear();
78 for (final Receiver receiver : children) {
79 childMailboxes.add(this.getCommunicationTracker().proxifyMailbox(this, receiver.getMailbox()));
80 }
81 }
82
83 @Override
84 public Collection<Receiver> getReceivers() {
85 return children;
86 }
87
88 /**
89 * @since 2.2
90 */
91 public Collection<Mailbox> getChildMailboxes() {
92 return this.childMailboxes;
93 }
94
95 @Override
96 public Set<Tuple> getPulledContents(final boolean flush) {
97 final HashSet<Tuple> results = new HashSet<Tuple>();
98 pullInto(results, flush);
99 return results;
100 }
101
102 @Override
103 public ProjectionIndexer constructIndex(final TupleMask mask, final TraceInfo... traces) {
104 final GenericProjectionIndexer indexer = new GenericProjectionIndexer(reteContainer, mask);
105 for (final TraceInfo traceInfo : traces) {
106 indexer.assignTraceInfo(traceInfo);
107 }
108 reteContainer.connectAndSynchronize(this, indexer);
109 return indexer;
110 }
111
112 /**
113 * @since 1.6
114 */
115 protected void issueError(final String message, final Exception ex) {
116 if (ex == null) {
117 this.reteContainer.getNetwork().getEngine().getLogger().error(message);
118 } else {
119 this.reteContainer.getNetwork().getEngine().getLogger().error(message, ex);
120 }
121 }
122
123}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.network;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21import tools.refinery.viatra.runtime.rete.single.TrimmerNode;
22import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
23
24/**
25 * @author Gabor Bergmann
26 *
27 * A supplier is an object that can propagate insert or revoke events towards receivers.
28 */
29public interface Supplier extends Node {
30
31 /**
32 * Pulls the contents of this object in this particular moment into a target collection.
33 *
34 * @param flush if true, flushing of messages is allowed during the pull, otherwise flushing is not allowed
35 * @since 2.3
36 */
37 public void pullInto(Collection<Tuple> collector, boolean flush);
38
39 /**
40 * @since 2.4
41 */
42 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush);
43
44 /**
45 * Returns the contents of this object in this particular moment.
46 * For memoryless nodes, this may involve a costly recomputation of contents.
47 *
48 * The result is returned as a Set, even when it has multiplicities (at the output of {@link TrimmerNode}).
49 *
50 * <p> Intended mainly for debug purposes; therefore flushing is performed only if explicitly requested
51 * During runtime, flushing may be preferred; see {@link ReteContainer#pullContents(Supplier)}
52 * @since 2.3
53 */
54 public Set<Tuple> getPulledContents(boolean flush);
55
56 default public Set<Tuple> getPulledContents() {
57 return getPulledContents(true);
58 }
59
60 /**
61 * appends a receiver that will continously receive insert and revoke updates from this supplier
62 */
63 void appendChild(Receiver receiver);
64
65 /**
66 * removes a receiver
67 */
68 void removeChild(Receiver receiver);
69
70 /**
71 * Instantiates (or reuses, depending on implementation) an index according to the given mask.
72 *
73 * Intended for internal use; clients should invoke through Library instead to enable reusing.
74 */
75 ProjectionIndexer constructIndex(TupleMask mask, TraceInfo... traces);
76
77 /**
78 * lists receivers
79 */
80 Collection<Receiver> getReceivers();
81
82}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.network;
11
12/**
13 * @author Gabor Bergmann
14 *
15 * A Tunnel is an interface into which elments can be instered and from which productions can be extracted.
16 */
17public interface Tunnel extends Supplier, Receiver {
18
19}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.network;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.util.Direction;
14
15class UpdateMessage {
16 public Receiver receiver;
17 public Direction direction;
18 public Tuple updateElement;
19
20 public UpdateMessage(Receiver receiver, Direction direction, Tuple updateElement) {
21 this.receiver = receiver;
22 this.direction = direction;
23 this.updateElement = updateElement;
24 }
25
26 @Override
27 public String toString() {
28 return "M." + direction + ": " + updateElement + " -> " + receiver;
29 }
30
31}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication;
10
11import java.util.Collection;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.rete.network.Node;
15import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
16
17/**
18 * A communication group represents a set of nodes in the communication graph that form a strongly connected component.
19 *
20 * @author Tamas Szabo
21 * @since 1.6
22 */
23public abstract class CommunicationGroup implements Comparable<CommunicationGroup> {
24
25 public static final String UNSUPPORTED_MESSAGE_KIND = "Unsupported message kind ";
26
27 /**
28 * Marker for the {@link CommunicationTracker}
29 */
30 public boolean isEnqueued = false;
31
32 protected final Node representative;
33
34 /**
35 * May be changed during bumping in {@link CommunicationTracker.registerDependency}
36 */
37 protected int identifier;
38
39 /**
40 * @since 1.7
41 */
42 protected final CommunicationTracker tracker;
43
44 /**
45 * @since 1.7
46 */
47 public CommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) {
48 this.tracker = tracker;
49 this.representative = representative;
50 this.identifier = identifier;
51 }
52
53 public abstract void deliverMessages();
54
55 public Node getRepresentative() {
56 return representative;
57 }
58
59 public abstract boolean isEmpty();
60
61 /**
62 * @since 2.0
63 */
64 public abstract void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind);
65
66 /**
67 * @since 2.0
68 */
69 public abstract void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind);
70
71 public abstract Map<MessageSelector, Collection<Mailbox>> getMailboxes();
72
73 public abstract boolean isRecursive();
74
75 @Override
76 public int hashCode() {
77 return this.identifier;
78 }
79
80 @Override
81 public String toString() {
82 return this.getClass().getSimpleName() + " " + this.identifier + " - representative: " + this.representative
83 + " - isEmpty: " + isEmpty();
84 }
85
86 @Override
87 public boolean equals(final Object obj) {
88 if (obj == null || this.getClass() != obj.getClass()) {
89 return false;
90 } else if (this == obj) {
91 return true;
92 } else {
93 final CommunicationGroup that = (CommunicationGroup) obj;
94 return this.identifier == that.identifier;
95 }
96 }
97
98 @Override
99 public int compareTo(final CommunicationGroup that) {
100 return this.identifier - that.identifier;
101 }
102
103}
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..d244e644
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java
@@ -0,0 +1,467 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication;
10
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.List;
14import java.util.Map;
15import java.util.PriorityQueue;
16import java.util.Queue;
17import java.util.Set;
18
19import tools.refinery.viatra.runtime.rete.itc.alg.incscc.IncSCCAlg;
20import tools.refinery.viatra.runtime.rete.itc.alg.misc.topsort.TopologicalSorting;
21import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph;
22import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
23import tools.refinery.viatra.runtime.rete.aggregation.IAggregatorNode;
24import tools.refinery.viatra.runtime.rete.boundary.ExternalInputEnumeratorNode;
25import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode;
26import tools.refinery.viatra.runtime.rete.index.DualInputNode;
27import tools.refinery.viatra.runtime.rete.index.ExistenceNode;
28import tools.refinery.viatra.runtime.rete.index.Indexer;
29import tools.refinery.viatra.runtime.rete.index.IndexerListener;
30import tools.refinery.viatra.runtime.rete.index.IterableIndexer;
31import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer;
32import tools.refinery.viatra.runtime.rete.network.IGroupable;
33import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
34import tools.refinery.viatra.runtime.rete.network.Node;
35import tools.refinery.viatra.runtime.rete.network.ProductionNode;
36import tools.refinery.viatra.runtime.rete.network.Receiver;
37import tools.refinery.viatra.runtime.rete.network.ReteContainer;
38import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyIndexerListenerProxy;
39import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyMailboxProxy;
40import tools.refinery.viatra.runtime.rete.network.mailbox.FallThroughCapableMailbox;
41import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
42import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
43import tools.refinery.viatra.runtime.rete.single.TransitiveClosureNode;
44import tools.refinery.viatra.runtime.rete.single.TrimmerNode;
45
46/**
47 * An instance of this class is associated with every {@link ReteContainer}. The tracker serves two purposes: <br>
48 * (1) It allows RETE nodes to register their communication dependencies on-the-fly. These dependencies can be
49 * registered or unregistered when nodes are disposed of. <br>
50 * (2) It allows RETE nodes to register their mailboxes as dirty, that is, they can tell the tracker that they have
51 * something to send to other nodes in the network. The tracker is then responsible for ordering these messages (more
52 * precisely, the mailboxes that contain the messages) for the associated {@link ReteContainer}. The ordering is
53 * governed by the strongly connected components in the dependency network and follows a topological sorting scheme;
54 * those mailboxes will be emptied first whose owner nodes do not depend on other undelivered messages.
55 *
56 * @author Tamas Szabo
57 * @since 1.6
58 *
59 */
60public abstract class CommunicationTracker {
61
62 /**
63 * The minimum group id assigned so far
64 */
65 protected int minGroupId;
66
67 /**
68 * The maximum group id assigned so far
69 */
70 protected int maxGroupId;
71
72 /**
73 * The dependency graph of the communications in the RETE network
74 */
75 protected final Graph<Node> dependencyGraph;
76
77 /**
78 * Incremental SCC information about the dependency graph
79 */
80 protected final IncSCCAlg<Node> sccInformationProvider;
81
82 /**
83 * Precomputed node -> communication group map
84 */
85 protected final Map<Node, CommunicationGroup> groupMap;
86
87 /**
88 * Priority queue of active communication groups
89 */
90 protected final Queue<CommunicationGroup> groupQueue;
91
92 // groups should have a simple integer flag which represents its position in a priority queue
93 // priority queue only contains the ACTIVE groups
94
95 public CommunicationTracker() {
96 this.dependencyGraph = new Graph<Node>();
97 this.sccInformationProvider = new IncSCCAlg<Node>(this.dependencyGraph);
98 this.groupQueue = new PriorityQueue<CommunicationGroup>();
99 this.groupMap = new HashMap<Node, CommunicationGroup>();
100 }
101
102 public Graph<Node> getDependencyGraph() {
103 return dependencyGraph;
104 }
105
106 public CommunicationGroup getGroup(final Node node) {
107 return this.groupMap.get(node);
108 }
109
110 private void precomputeGroups() {
111 groupMap.clear();
112
113 // reconstruct group map from dependency graph
114 final Graph<Node> reducedGraph = sccInformationProvider.getReducedGraph();
115 final List<Node> representatives = TopologicalSorting.compute(reducedGraph);
116
117 for (int i = 0; i < representatives.size(); i++) { // groups for SCC representatives
118 final Node representative = representatives.get(i);
119 createAndStoreGroup(representative, i);
120 }
121
122 minGroupId = 0;
123 maxGroupId = representatives.size() - 1;
124
125 for (final Node node : dependencyGraph.getAllNodes()) { // extend group map to the rest of nodes
126 final Node representative = sccInformationProvider.getRepresentative(node);
127 final CommunicationGroup group = groupMap.get(representative);
128 if (representative != node) {
129 addToGroup(node, group);
130 }
131 }
132
133 for (final Node node : dependencyGraph.getAllNodes()) {
134 // set fall-through flags of default mailboxes
135 precomputeFallThroughFlag(node);
136 // perform further tracker-specific post-processing
137 postProcessNode(node);
138 }
139
140 // reconstruct new queue contents based on new group map
141 if (!groupQueue.isEmpty()) {
142 final Set<CommunicationGroup> oldActiveGroups = new HashSet<CommunicationGroup>(groupQueue);
143 groupQueue.clear();
144 reconstructQueueContents(oldActiveGroups);
145 }
146
147 // post process the groups
148 for (final CommunicationGroup group : groupMap.values()) {
149 postProcessGroup(group);
150 }
151 }
152
153 /**
154 * This method is responsible for reconstructing the active queue contents after the network structure has changed.
155 * It it defined as abstract because the reconstruction logic is specific to each {@link CommunicationTracker}.
156 * @since 2.4
157 */
158 protected abstract void reconstructQueueContents(final Set<CommunicationGroup> oldActiveGroups);
159
160 private void addToGroup(final Node node, final CommunicationGroup group) {
161 groupMap.put(node, group);
162 if (node instanceof Receiver) {
163 ((Receiver) node).getMailbox().setCurrentGroup(group);
164 if (node instanceof IGroupable) {
165 ((IGroupable) node).setCurrentGroup(group);
166 }
167 }
168 }
169
170 /**
171 * Depends on the groups, as well as the parent nodes of the argument, so recomputation is needed if these change
172 */
173 private void precomputeFallThroughFlag(final Node node) {
174 CommunicationGroup group = groupMap.get(node);
175 if (node instanceof Receiver) {
176 IGroupable mailbox = ((Receiver) node).getMailbox();
177 if (mailbox instanceof FallThroughCapableMailbox) {
178 Set<Node> directParents = dependencyGraph.getSourceNodes(node).distinctValues();
179 // decide between using quick&cheap fall-through, or allowing for update cancellation
180 boolean fallThrough =
181 // disallow fallthrough: updates at production nodes should cancel, if they can be trimmed or
182 // disjunctive
183 (!(node instanceof ProductionNode && ( // it is a production node...
184 // with more than one parent
185 directParents.size() > 0 ||
186 // or true trimming in its sole parent
187 directParents.size() == 1 && trueTrimming(directParents.iterator().next())))) &&
188 // disallow fallthrough: external updates should be stored (if updates are delayed)
189 (!(node instanceof ExternalInputEnumeratorNode)) &&
190 // disallow fallthrough: RelationEvaluatorNode needs to be notified in batch-style, and the batching is done by the mailbox
191 // however, it is not the RelationEvaluatorNode itself that is interesting here, as that indirectly uses the BatchingReceiver
192 // so we need to disable fall-through for the BatchingReceiver
193 (!(node instanceof RelationEvaluatorNode.BatchingReceiver));
194 // do additional checks
195 if (fallThrough) {
196 // recursive parent groups generate excess updates that should be cancelled after delete&rederive
197 // phases
198 // aggregator and transitive closure parent nodes also generate excess updates that should be
199 // cancelled
200 directParentLoop: for (Node directParent : directParents) {
201 Set<Node> parentsToCheck = new HashSet<>();
202 // check the case where a direct parent is the reason for mailbox usage
203 parentsToCheck.add(directParent);
204 // check the case where an indirect parent (join slot) is the reason for mailbox usage
205 if (directParent instanceof DualInputNode) {
206 // in case of existence join (typically antijoin), a mailbox should allow
207 // an insertion and deletion (at the secondary slot) to cancel each other out
208 if (directParent instanceof ExistenceNode) {
209 fallThrough = false;
210 break directParentLoop;
211 }
212 // in beta nodes, indexer slots (or their active nodes) are considered indirect parents
213 DualInputNode dualInput = (DualInputNode) directParent;
214 IterableIndexer primarySlot = dualInput.getPrimarySlot();
215 if (primarySlot != null)
216 parentsToCheck.add(primarySlot.getActiveNode());
217 Indexer secondarySlot = dualInput.getSecondarySlot();
218 if (secondarySlot != null)
219 parentsToCheck.add(secondarySlot.getActiveNode());
220 }
221 for (Node parent : parentsToCheck) {
222 CommunicationGroup parentGroup = groupMap.get(parent);
223 if ( // parent is in a different, recursive group
224 (group != parentGroup && parentGroup.isRecursive()) ||
225 // node and parent within the same recursive group, and...
226 (group == parentGroup && group.isRecursive() && (
227 // parent is a transitive closure or aggregator node, or a trimmer
228 // allow trimmed or disjunctive tuple updates to cancel each other
229 (parent instanceof TransitiveClosureNode) || (parent instanceof IAggregatorNode)
230 || trueTrimming(parent)))) {
231 fallThrough = false;
232 break directParentLoop;
233 }
234 }
235 }
236 }
237 // overwrite fallthrough flag with newly computed value
238 ((FallThroughCapableMailbox) mailbox).setFallThrough(fallThrough);
239 }
240 }
241 }
242
243 /**
244 * A trimmer node that actually eliminates some columns (not just reorders)
245 */
246 private boolean trueTrimming(Node node) {
247 if (node instanceof TrimmerNode) {
248 TupleMask mask = ((TrimmerNode) node).getMask();
249 return (mask.indices.length != mask.sourceWidth);
250 }
251 return false;
252 }
253
254 public void activateUnenqueued(final CommunicationGroup group) {
255 groupQueue.add(group);
256 group.isEnqueued = true;
257 }
258
259 public void deactivate(final CommunicationGroup group) {
260 groupQueue.remove(group);
261 group.isEnqueued = false;
262 }
263
264 public CommunicationGroup getAndRemoveFirstGroup() {
265 final CommunicationGroup group = groupQueue.poll();
266 group.isEnqueued = false;
267 return group;
268 }
269
270 public boolean isEmpty() {
271 return groupQueue.isEmpty();
272 }
273
274 protected abstract CommunicationGroup createGroup(final Node representative, final int index);
275
276 protected CommunicationGroup createAndStoreGroup(final Node representative, final int index) {
277 final CommunicationGroup group = createGroup(representative, index);
278 addToGroup(representative, group);
279 return group;
280 }
281
282 /**
283 * Registers the dependency that the target {@link Node} depends on the source {@link Node}. In other words, source
284 * may send messages to target in the RETE network. If the dependency edge is already present, this method call is a
285 * noop.
286 *
287 * @param source
288 * the source node
289 * @param target
290 * the target node
291 */
292 public void registerDependency(final Node source, final Node target) {
293 // nodes can be immediately inserted, if they already exist in the graph, this is a noop
294 dependencyGraph.insertNode(source);
295 dependencyGraph.insertNode(target);
296
297 if (!this.dependencyGraph.getTargetNodes(source).containsNonZero(target)) {
298
299 // query all these information before the actual edge insertion
300 // because SCCs may be unified during the process
301 final Node sourceRepresentative = sccInformationProvider.getRepresentative(source);
302 final Node targetRepresentative = sccInformationProvider.getRepresentative(target);
303 final boolean targetHadOutgoingEdges = sccInformationProvider.hasOutgoingEdges(targetRepresentative);
304
305 // insert the edge
306 dependencyGraph.insertEdge(source, target);
307
308 // create groups if they do not yet exist
309 CommunicationGroup sourceGroup = groupMap.get(sourceRepresentative);
310 if (sourceGroup == null) {
311 // create on-demand with the next smaller group id
312 sourceGroup = createAndStoreGroup(sourceRepresentative, --minGroupId);
313 }
314 final int sourceIndex = sourceGroup.identifier;
315
316 CommunicationGroup targetGroup = groupMap.get(targetRepresentative);
317 if (targetGroup == null) {
318 // create on-demand with the next larger group id
319 targetGroup = createAndStoreGroup(targetRepresentative, ++maxGroupId);
320 }
321 final int targetIndex = targetGroup.identifier;
322
323 if (sourceIndex <= targetIndex) {
324 // indices obey current topological ordering
325 refreshFallThroughFlag(target);
326 postProcessNode(source);
327 postProcessNode(target);
328 postProcessGroup(sourceGroup);
329 if (sourceGroup != targetGroup) {
330 postProcessGroup(targetGroup);
331 }
332 } else if (sourceIndex > targetIndex && !targetHadOutgoingEdges) {
333 // indices violate current topological ordering, but we can simply bump the target index
334 final boolean wasEnqueued = targetGroup.isEnqueued;
335 if (wasEnqueued) {
336 groupQueue.remove(targetGroup);
337 }
338 targetGroup.identifier = ++maxGroupId;
339 if (wasEnqueued) {
340 groupQueue.add(targetGroup);
341 }
342
343 refreshFallThroughFlag(target);
344 postProcessNode(source);
345 postProcessNode(target);
346 postProcessGroup(sourceGroup);
347 postProcessGroup(targetGroup);
348 } else {
349 // needs a full re-computation because of more complex change
350 precomputeGroups();
351 }
352 }
353 }
354
355 /**
356 * Returns true if the given {@link Node} is in a recursive {@link CommunicationGroup}, false otherwise.
357 */
358 public boolean isInRecursiveGroup(final Node node) {
359 final CommunicationGroup group = this.getGroup(node);
360 if (group == null) {
361 return false;
362 } else {
363 return group.isRecursive();
364 }
365 }
366
367 /**
368 * Returns true if the given two {@link Node}s are in the same {@link CommunicationGroup}.
369 */
370 public boolean areInSameGroup(final Node left, final Node right) {
371 final CommunicationGroup leftGroup = this.getGroup(left);
372 final CommunicationGroup rightGroup = this.getGroup(right);
373 return leftGroup != null && leftGroup == rightGroup;
374 }
375
376 /**
377 * Unregisters a dependency between source and target.
378 *
379 * @param source
380 * the source node
381 * @param target
382 * the target node
383 */
384 public void unregisterDependency(final Node source, final Node target) {
385 // delete the edge first, and then query the SCC info provider
386 this.dependencyGraph.deleteEdgeIfExists(source, target);
387
388 final Node sourceRepresentative = sccInformationProvider.getRepresentative(source);
389 final Node targetRepresentative = sccInformationProvider.getRepresentative(target);
390
391 // if they are still in the same SCC,
392 // then this deletion did not affect the SCCs,
393 // and it is sufficient to recompute affected fall-through flags;
394 // otherwise, we need a new pre-computation for the groupMap and groupQueue
395 if (sourceRepresentative.equals(targetRepresentative)) {
396 // this deletion could not have affected the split flags
397 refreshFallThroughFlag(target);
398 postProcessNode(source);
399 postProcessNode(target);
400 } else {
401 // preComputeGroups takes care of the split flag maintenance
402 precomputeGroups();
403 }
404 }
405
406 /**
407 * Refresh fall-through flags if dependencies change for given target, but no SCC change
408 */
409 private void refreshFallThroughFlag(final Node target) {
410 precomputeFallThroughFlag(target);
411 if (target instanceof DualInputNode) {
412 for (final Node indirectTarget : dependencyGraph.getTargetNodes(target).distinctValues()) {
413 precomputeFallThroughFlag(indirectTarget);
414 }
415 }
416 }
417
418 /**
419 * Returns true if the given source-target edge in the communication network acts as a recursion cut point.
420 * The current implementation considers edges leading into {@link ProductionNode}s as cut point iff
421 * both source and target belong to the same group.
422 *
423 * @param source the source node
424 * @param target the target node
425 * @return true if the edge is a cut point, false otherwise
426 * @since 2.4
427 */
428 protected boolean isRecursionCutPoint(final Node source, final Node target) {
429 final Node effectiveSource = source instanceof SpecializedProjectionIndexer
430 ? ((SpecializedProjectionIndexer) source).getActiveNode()
431 : source;
432 final CommunicationGroup sourceGroup = this.getGroup(effectiveSource);
433 final CommunicationGroup targetGroup = this.getGroup(target);
434 return sourceGroup != null && sourceGroup == targetGroup && target instanceof ProductionNode;
435 }
436
437 /**
438 * This hook allows concrete tracker implementations to perform tracker-specific post processing on nodes (cf.
439 * {@link NetworkStructureChangeSensitiveNode} and {@link BehaviorChangingMailbox}). At the time of the invocation,
440 * the network topology has already been updated.
441 */
442 protected abstract void postProcessNode(final Node node);
443
444 /**
445 * This hook allows concrete tracker implementations to perform tracker-specific post processing on groups. At the
446 * time of the invocation, the network topology has already been updated.
447 * @since 2.4
448 */
449 protected abstract void postProcessGroup(final CommunicationGroup group);
450
451 /**
452 * Creates a proxy for the given {@link Mailbox} for the given requester {@link Node}. The proxy creation is
453 * {@link CommunicationTracker}-specific and depends on the identity of the requester. This method is primarily used
454 * to create {@link TimelyMailboxProxy}s depending on the network topology. There is no guarantee that the same
455 * proxy instance is returned when this method is called multiple times with the same arguments.
456 */
457 public abstract Mailbox proxifyMailbox(final Node requester, final Mailbox original);
458
459 /**
460 * Creates a proxy for the given {@link IndexerListener} for the given requester {@link Node}. The proxy creation is
461 * {@link CommunicationTracker}-specific and depends on the identity of the requester. This method is primarily used
462 * to create {@link TimelyIndexerListenerProxy}s depending on the network topology. There is no guarantee that the
463 * same proxy instance is returned when this method is called multiple times with the same arguments.
464 */
465 public abstract IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original);
466
467}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication;
10
11/**
12 * Subclasses of this interface represent meta data of update messages in Rete.
13 *
14 * @author Tamas Szabo
15 * @since 2.3
16 */
17public interface MessageSelector {
18
19}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication;
10
11import java.util.Comparator;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.rete.network.Node;
15
16/**
17 * @since 2.4
18 */
19public class NodeComparator implements Comparator<Node> {
20
21 protected final Map<Node, Integer> nodeMap;
22
23 public NodeComparator(final Map<Node, Integer> nodeMap) {
24 this.nodeMap = nodeMap;
25 }
26
27 @Override
28 public int compare(final Node left, final Node right) {
29 return this.nodeMap.get(left) - this.nodeMap.get(right);
30 }
31
32} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication;
10
11/**
12 * A default message selector that can be used to associate phases to messages.
13 *
14 * @author Tamas Szabo
15 * @since 2.3
16 */
17public enum PhasedSelector implements MessageSelector {
18
19 /**
20 * No special distinguishing feature
21 */
22 DEFAULT,
23
24 /**
25 * Inserts and delete-insert monotone change pairs
26 */
27 MONOTONE,
28
29 /**
30 * Deletes
31 */
32 ANTI_MONOTONE
33
34}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication;
10
11import java.util.AbstractMap;
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines;
18
19/**
20 * A timestamp associated with update messages in timely evaluation.
21 *
22 * @author Tamas Szabo
23 * @since 2.3
24 */
25public class Timestamp implements Comparable<Timestamp>, MessageSelector {
26
27 protected final int value;
28 public static final Timestamp ZERO = new Timestamp(0);
29 /**
30 * @since 2.4
31 */
32 public static final Timeline<Timestamp> INSERT_AT_ZERO_TIMELINE = Timelines.createFrom(Timestamp.ZERO);
33
34 public Timestamp(final int value) {
35 this.value = value;
36 }
37
38 public int getValue() {
39 return value;
40 }
41
42 public Timestamp max(final Timestamp that) {
43 if (this.value >= that.value) {
44 return this;
45 } else {
46 return that;
47 }
48 }
49
50 /**
51 * @since 2.4
52 */
53 public Timestamp min(final Timestamp that) {
54 if (this.value <= that.value) {
55 return this;
56 } else {
57 return that;
58 }
59 }
60
61 @Override
62 public int compareTo(final Timestamp that) {
63 return this.value - that.value;
64 }
65
66 @Override
67 public boolean equals(final Object obj) {
68 if (obj == null || !(obj instanceof Timestamp)) {
69 return false;
70 } else {
71 return this.value == ((Timestamp) obj).value;
72 }
73 }
74
75 @Override
76 public int hashCode() {
77 return this.value;
78 }
79
80 @Override
81 public String toString() {
82 return Integer.toString(this.value);
83 }
84
85 /**
86 * A {@link Map} implementation that associates the zero timestamp with every key. There is no suppor for
87 * {@link Map#entrySet()} due to performance reasons.
88 *
89 * @author Tamas Szabo
90 */
91 public static final class AllZeroMap<T> extends AbstractMap<T, Timeline<Timestamp>> {
92
93 private final Collection<T> wrapped;
94
95 public AllZeroMap(Set<T> wrapped) {
96 this.wrapped = wrapped;
97 }
98
99 @Override
100 public Set<Entry<T, Timeline<Timestamp>>> entrySet() {
101 throw new UnsupportedOperationException("Use the combination of keySet() and get()!");
102 }
103
104 /**
105 * @since 2.4
106 */
107 @Override
108 public Timeline<Timestamp> get(final Object key) {
109 return INSERT_AT_ZERO_TIMELINE;
110 }
111
112 @Override
113 public Set<T> keySet() {
114 return (Set<T>) this.wrapped;
115 }
116
117 @Override
118 public String toString() {
119 return this.getClass().getSimpleName() + ": " + this.keySet().toString();
120 }
121
122 }
123
124}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication.timeless;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.EnumMap;
14import java.util.LinkedHashSet;
15import java.util.Map;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.rete.network.Node;
20import tools.refinery.viatra.runtime.rete.network.RederivableNode;
21import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
22import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
23import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
24import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector;
25import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
26
27/**
28 * A communication group representing either a single node where the
29 * node is a monotonicity aware one or a set of nodes that form an SCC.
30 *
31 * @author Tamas Szabo
32 * @since 2.4
33 */
34public class RecursiveCommunicationGroup extends CommunicationGroup {
35
36 private final Set<Mailbox> antiMonotoneMailboxes;
37 private final Set<Mailbox> monotoneMailboxes;
38 private final Set<Mailbox> defaultMailboxes;
39 private final Set<RederivableNode> rederivables;
40 private boolean currentlyDelivering;
41
42 /**
43 * @since 1.7
44 */
45 public RecursiveCommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) {
46 super(tracker, representative, identifier);
47 this.antiMonotoneMailboxes = CollectionsFactory.createSet();
48 this.monotoneMailboxes = CollectionsFactory.createSet();
49 this.defaultMailboxes = CollectionsFactory.createSet();
50 this.rederivables = new LinkedHashSet<RederivableNode>();
51 this.currentlyDelivering = false;
52 }
53
54 @Override
55 public void deliverMessages() {
56 this.currentlyDelivering = true;
57
58 // ANTI-MONOTONE PHASE
59 while (!this.antiMonotoneMailboxes.isEmpty() || !this.defaultMailboxes.isEmpty()) {
60 while (!this.antiMonotoneMailboxes.isEmpty()) {
61 final Mailbox mailbox = this.antiMonotoneMailboxes.iterator().next();
62 this.antiMonotoneMailboxes.remove(mailbox);
63 mailbox.deliverAll(PhasedSelector.ANTI_MONOTONE);
64 }
65 while (!this.defaultMailboxes.isEmpty()) {
66 final Mailbox mailbox = this.defaultMailboxes.iterator().next();
67 this.defaultMailboxes.remove(mailbox);
68 mailbox.deliverAll(PhasedSelector.DEFAULT);
69 }
70 }
71
72 // REDERIVE PHASE
73 while (!this.rederivables.isEmpty()) {
74 // re-derivable nodes take care of their unregistration!!
75 final RederivableNode node = this.rederivables.iterator().next();
76 node.rederiveOne();
77 }
78
79 // MONOTONE PHASE
80 while (!this.monotoneMailboxes.isEmpty() || !this.defaultMailboxes.isEmpty()) {
81 while (!this.monotoneMailboxes.isEmpty()) {
82 final Mailbox mailbox = this.monotoneMailboxes.iterator().next();
83 this.monotoneMailboxes.remove(mailbox);
84 mailbox.deliverAll(PhasedSelector.MONOTONE);
85 }
86 while (!this.defaultMailboxes.isEmpty()) {
87 final Mailbox mailbox = this.defaultMailboxes.iterator().next();
88 this.defaultMailboxes.remove(mailbox);
89 mailbox.deliverAll(PhasedSelector.DEFAULT);
90 }
91 }
92
93 this.currentlyDelivering = false;
94 }
95
96 @Override
97 public boolean isEmpty() {
98 return this.rederivables.isEmpty() && this.antiMonotoneMailboxes.isEmpty()
99 && this.monotoneMailboxes.isEmpty() && this.defaultMailboxes.isEmpty();
100 }
101
102 @Override
103 public void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind) {
104 final Collection<Mailbox> mailboxes = getMailboxContainer(kind);
105 mailboxes.add(mailbox);
106 if (!this.isEnqueued && !this.currentlyDelivering) {
107 this.tracker.activateUnenqueued(this);
108 }
109 }
110
111 @Override
112 public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) {
113 final Collection<Mailbox> mailboxes = getMailboxContainer(kind);
114 mailboxes.remove(mailbox);
115 if (isEmpty()) {
116 this.tracker.deactivate(this);
117 }
118 }
119
120 private Collection<Mailbox> getMailboxContainer(final MessageSelector kind) {
121 if (kind == PhasedSelector.ANTI_MONOTONE) {
122 return this.antiMonotoneMailboxes;
123 } else if (kind == PhasedSelector.MONOTONE) {
124 return this.monotoneMailboxes;
125 } else if (kind == PhasedSelector.DEFAULT) {
126 return this.defaultMailboxes;
127 } else {
128 throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind);
129 }
130 }
131
132 public void addRederivable(final RederivableNode node) {
133 this.rederivables.add(node);
134 if (!this.isEnqueued) {
135 this.tracker.activateUnenqueued(this);
136 }
137 }
138
139 public void removeRederivable(final RederivableNode node) {
140 this.rederivables.remove(node);
141 if (isEmpty()) {
142 this.tracker.deactivate(this);
143 }
144 }
145
146 public Collection<RederivableNode> getRederivables() {
147 return this.rederivables;
148 }
149
150 @Override
151 public Map<MessageSelector, Collection<Mailbox>> getMailboxes() {
152 Map<PhasedSelector, Collection<Mailbox>> map = new EnumMap<>(PhasedSelector.class);
153 map.put(PhasedSelector.ANTI_MONOTONE, antiMonotoneMailboxes);
154 map.put(PhasedSelector.MONOTONE, monotoneMailboxes);
155 map.put(PhasedSelector.DEFAULT, defaultMailboxes);
156 return Collections.unmodifiableMap(map);
157 }
158
159 @Override
160 public boolean isRecursive() {
161 return true;
162 }
163
164}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication.timeless;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.rete.network.Node;
16import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
17import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
18import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
19import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector;
20import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
21
22/**
23 * A communication group containing only a single node with a single default
24 * mailbox.
25 *
26 * @author Tamas Szabo
27 * @since 1.6
28 */
29public class SingletonCommunicationGroup extends CommunicationGroup {
30
31 private Mailbox mailbox;
32
33 /**
34 * @since 1.7
35 */
36 public SingletonCommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) {
37 super(tracker, representative, identifier);
38 }
39
40 @Override
41 public void deliverMessages() {
42 this.mailbox.deliverAll(PhasedSelector.DEFAULT);
43 }
44
45 @Override
46 public boolean isEmpty() {
47 return this.mailbox == null;
48 }
49
50 @Override
51 public void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind) {
52 if (kind == PhasedSelector.DEFAULT) {
53 this.mailbox = mailbox;
54 if (!this.isEnqueued) {
55 this.tracker.activateUnenqueued(this);
56 }
57 } else {
58 throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind);
59 }
60 }
61
62 @Override
63 public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) {
64 if (kind == PhasedSelector.DEFAULT) {
65 this.mailbox = null;
66 this.tracker.deactivate(this);
67 } else {
68 throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind);
69 }
70 }
71
72 @Override
73 public Map<MessageSelector, Collection<Mailbox>> getMailboxes() {
74 if (mailbox != null) {
75 return Collections.singletonMap(PhasedSelector.DEFAULT, Collections.singleton(mailbox));
76 } else {
77 return Collections.emptyMap();
78 }
79 }
80
81 @Override
82 public boolean isRecursive() {
83 return false;
84 }
85
86}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication.timeless;
10
11import java.util.Collection;
12import java.util.HashSet;
13import java.util.Set;
14import java.util.Map.Entry;
15
16import tools.refinery.viatra.runtime.rete.index.DualInputNode;
17import tools.refinery.viatra.runtime.rete.index.Indexer;
18import tools.refinery.viatra.runtime.rete.index.IndexerListener;
19import tools.refinery.viatra.runtime.rete.index.IterableIndexer;
20import tools.refinery.viatra.runtime.rete.network.Node;
21import tools.refinery.viatra.runtime.rete.network.Receiver;
22import tools.refinery.viatra.runtime.rete.network.RederivableNode;
23import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
24import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
25import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
26import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
27import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
28
29/**
30 * Timeless implementation of the communication tracker.
31 *
32 * @author Tamas Szabo
33 * @since 2.2
34 */
35public class TimelessCommunicationTracker extends CommunicationTracker {
36
37 @Override
38 protected CommunicationGroup createGroup(Node representative, int index) {
39 final boolean isSingleton = this.sccInformationProvider.sccs.getPartition(representative).size() == 1;
40 final boolean isReceiver = representative instanceof Receiver;
41 final boolean isPosetIndifferent = isReceiver
42 && ((Receiver) representative).getMailbox() instanceof BehaviorChangingMailbox;
43 final boolean isSingletonInDRedMode = isSingleton && (representative instanceof RederivableNode)
44 && ((RederivableNode) representative).isInDRedMode();
45
46 CommunicationGroup group = null;
47 // we can only use a singleton group iff
48 // (1) the SCC has one node AND
49 // (2) either we have a poset-indifferent mailbox OR the node is not even a receiver AND
50 // (3) the node does not run in DRed mode in a singleton group
51 if (isSingleton && (isPosetIndifferent || !isReceiver) && !isSingletonInDRedMode) {
52 group = new SingletonCommunicationGroup(this, representative, index);
53 } else {
54 group = new RecursiveCommunicationGroup(this, representative, index);
55 }
56
57 return group;
58 }
59
60 @Override
61 protected void reconstructQueueContents(final Set<CommunicationGroup> oldActiveGroups) {
62 for (final CommunicationGroup oldGroup : oldActiveGroups) {
63 for (final Entry<MessageSelector, Collection<Mailbox>> entry : oldGroup.getMailboxes().entrySet()) {
64 for (final Mailbox mailbox : entry.getValue()) {
65 final CommunicationGroup newGroup = this.groupMap.get(mailbox.getReceiver());
66 newGroup.notifyHasMessage(mailbox, entry.getKey());
67 }
68 }
69
70 if (oldGroup instanceof RecursiveCommunicationGroup) {
71 for (final RederivableNode node : ((RecursiveCommunicationGroup) oldGroup).getRederivables()) {
72 final CommunicationGroup newGroup = this.groupMap.get(node);
73 if (!(newGroup instanceof RecursiveCommunicationGroup)) {
74 throw new IllegalStateException("The new group must also be recursive! " + newGroup);
75 }
76 ((RecursiveCommunicationGroup) newGroup).addRederivable(node);
77 }
78 }
79 }
80 }
81
82 @Override
83 public Mailbox proxifyMailbox(final Node requester, final Mailbox original) {
84 return original;
85 }
86
87 @Override
88 public IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original) {
89 return original;
90 }
91
92 @Override
93 protected void postProcessNode(final Node node) {
94 if (node instanceof Receiver) {
95 final Mailbox mailbox = ((Receiver) node).getMailbox();
96 if (mailbox instanceof BehaviorChangingMailbox) {
97 final CommunicationGroup group = this.groupMap.get(node);
98 final Set<Node> sccNodes = this.sccInformationProvider.sccs.getPartition(node);
99 // a default mailbox must split its messages iff
100 // (1) its receiver is in a recursive group and
101 final boolean c1 = group.isRecursive();
102 // (2) its receiver is at the SCC boundary of that group
103 final boolean c2 = isAtSCCBoundary(node);
104 // (3) its group consists of more than one node
105 final boolean c3 = sccNodes.size() > 1;
106 ((BehaviorChangingMailbox) mailbox).setSplitFlag(c1 && c2 && c3);
107 }
108 }
109 }
110
111 @Override
112 protected void postProcessGroup(final CommunicationGroup group) {
113
114 }
115
116 /**
117 * @since 2.0
118 */
119 private boolean isAtSCCBoundary(final Node node) {
120 final CommunicationGroup ownGroup = this.groupMap.get(node);
121 assert ownGroup != null;
122 for (final Node source : this.dependencyGraph.getSourceNodes(node).distinctValues()) {
123 final Set<Node> sourcesToCheck = new HashSet<Node>();
124 sourcesToCheck.add(source);
125 // DualInputNodes must be checked additionally because they do not use a mailbox directly.
126 // It can happen that their indexers actually belong to other SCCs.
127 if (source instanceof DualInputNode) {
128 final DualInputNode dualInput = (DualInputNode) source;
129 final IterableIndexer primarySlot = dualInput.getPrimarySlot();
130 if (primarySlot != null) {
131 sourcesToCheck.add(primarySlot.getActiveNode());
132 }
133 final Indexer secondarySlot = dualInput.getSecondarySlot();
134 if (secondarySlot != null) {
135 sourcesToCheck.add(secondarySlot.getActiveNode());
136 }
137 }
138 for (final Node current : sourcesToCheck) {
139 final CommunicationGroup otherGroup = this.groupMap.get(current);
140 assert otherGroup != null;
141 if (!ownGroup.equals(otherGroup)) {
142 return true;
143 }
144 }
145 }
146 return false;
147 }
148
149}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication.timely;
10
11import tools.refinery.viatra.runtime.rete.network.IGroupable;
12import tools.refinery.viatra.runtime.rete.network.Node;
13import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
14
15/**
16 * {@link Node}s that implement this interface can resume folding of their states when instructed during timely evaluation.
17 *
18 * @since 2.3
19 * @author Tamas Szabo
20 */
21public interface ResumableNode extends Node, IGroupable {
22
23 /**
24 * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to
25 * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more
26 * folding to do towards higher timestamps.
27 */
28 public void resumeAt(final Timestamp timestamp);
29
30 /**
31 * Returns the smallest timestamp where lazy folding shall be resumed, or null if there is no more folding to do in this
32 * resumable.
33 */
34 public Timestamp getResumableTimestamp();
35
36}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication.timely;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Comparator;
14import java.util.HashMap;
15import java.util.Map;
16import java.util.Map.Entry;
17import java.util.Set;
18import java.util.TreeMap;
19import java.util.TreeSet;
20
21import org.apache.log4j.Logger;
22import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
23import tools.refinery.viatra.runtime.rete.network.Node;
24import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
25import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
26import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
27import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
28import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
29import tools.refinery.viatra.runtime.rete.util.Options;
30
31/**
32 * A timely communication group implementation. {@link TimelyMailbox}es and {@link LazyFoldingNode}s are ordered in the
33 * increasing order of timestamps.
34 *
35 * @author Tamas Szabo
36 * @since 2.3
37 */
38public class TimelyCommunicationGroup extends CommunicationGroup {
39
40 private final boolean isSingleton;
41 private final TreeMap<Timestamp, Set<Mailbox>> mailboxQueue;
42 // may be null - only used in the scattered case where we need to take care of mailboxes and resumables too
43 private Comparator<Node> nodeComparator;
44 private boolean currentlyDelivering;
45 private Timestamp currentlyDeliveredTimestamp;
46
47 public TimelyCommunicationGroup(final TimelyCommunicationTracker tracker, final Node representative,
48 final int identifier, final boolean isSingleton) {
49 super(tracker, representative, identifier);
50 this.isSingleton = isSingleton;
51 this.mailboxQueue = CollectionsFactory.createTreeMap();
52 this.currentlyDelivering = false;
53 }
54
55 /**
56 * Sets the {@link Comparator} to be used to order the {@link Mailbox}es at a given {@link Timestamp} in the mailbox
57 * queue. Additionally, reorders already queued {@link Mailbox}es to reflect the new comparator. The comparator may
58 * be null, in this case, no set ordering will be enforced among the {@link Mailbox}es.
59 */
60 public void setComparatorAndReorderMailboxes(final Comparator<Node> nodeComparator) {
61 this.nodeComparator = nodeComparator;
62 if (!this.mailboxQueue.isEmpty()) {
63 final HashMap<Timestamp, Set<Mailbox>> queueCopy = new HashMap<Timestamp, Set<Mailbox>>(this.mailboxQueue);
64 this.mailboxQueue.clear();
65 for (final Entry<Timestamp, Set<Mailbox>> entry : queueCopy.entrySet()) {
66 for (final Mailbox mailbox : entry.getValue()) {
67 this.notifyHasMessage(mailbox, entry.getKey());
68 }
69 }
70 }
71 }
72
73 @Override
74 public void deliverMessages() {
75 this.currentlyDelivering = true;
76 while (!this.mailboxQueue.isEmpty()) {
77 // care must be taken here how we iterate over the mailboxes
78 // it is not okay to loop over the mailboxes at once because a mailbox may disappear from the collection as
79 // a result of delivering messages from another mailboxes under the same timestamp
80 // because of this, it is crucial that we pick the mailboxes one by one
81 final Entry<Timestamp, Set<Mailbox>> entry = this.mailboxQueue.firstEntry();
82 final Timestamp timestamp = entry.getKey();
83 final Set<Mailbox> mailboxes = entry.getValue();
84 final Mailbox mailbox = mailboxes.iterator().next();
85 mailboxes.remove(mailbox);
86 if (mailboxes.isEmpty()) {
87 this.mailboxQueue.pollFirstEntry();
88 }
89 assert mailbox instanceof TimelyMailbox;
90 /* debug */ this.currentlyDeliveredTimestamp = timestamp;
91 mailbox.deliverAll(timestamp);
92 /* debug */ this.currentlyDeliveredTimestamp = null;
93 }
94 this.currentlyDelivering = false;
95 }
96
97 @Override
98 public boolean isEmpty() {
99 return this.mailboxQueue.isEmpty();
100 }
101
102 @Override
103 public void notifyHasMessage(final Mailbox mailbox, MessageSelector kind) {
104 if (kind instanceof Timestamp) {
105 final Timestamp timestamp = (Timestamp) kind;
106 if (Options.MONITOR_VIOLATION_OF_DIFFERENTIAL_DATAFLOW_TIMESTAMPS) {
107 if (timestamp.compareTo(this.currentlyDeliveredTimestamp) < 0) {
108 final Logger logger = this.representative.getContainer().getNetwork().getEngine().getLogger();
109 logger.error(
110 "[INTERNAL ERROR] Violation of differential dataflow communication schema! The communication component with representative "
111 + this.representative + " observed decreasing timestamp during message delivery!");
112 }
113 }
114 final Set<Mailbox> mailboxes = this.mailboxQueue.computeIfAbsent(timestamp, k -> {
115 if (this.nodeComparator == null) {
116 return CollectionsFactory.createSet();
117 } else {
118 return new TreeSet<Mailbox>(new Comparator<Mailbox>() {
119 @Override
120 public int compare(final Mailbox left, final Mailbox right) {
121 return nodeComparator.compare(left.getReceiver(), right.getReceiver());
122 }
123 });
124 }
125 });
126 mailboxes.add(mailbox);
127 if (!this.isEnqueued && !this.currentlyDelivering) {
128 this.tracker.activateUnenqueued(this);
129 }
130 } else {
131 throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind);
132 }
133 }
134
135 @Override
136 public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) {
137 if (kind instanceof Timestamp) {
138 final Timestamp timestamp = (Timestamp) kind;
139 this.mailboxQueue.compute(timestamp, (k, v) -> {
140 if (v == null) {
141 throw new IllegalStateException("No mailboxes registered at timestamp " + timestamp + "!");
142 }
143 if (!v.remove(mailbox)) {
144 throw new IllegalStateException(
145 "The mailbox " + mailbox + " was not registered at timestamp " + timestamp + "!");
146 }
147 if (v.isEmpty()) {
148 return null;
149 } else {
150 return v;
151 }
152 });
153 if (this.mailboxQueue.isEmpty()) {
154 this.tracker.deactivate(this);
155 }
156 } else {
157 throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind);
158 }
159 }
160
161 @Override
162 public Map<MessageSelector, Collection<Mailbox>> getMailboxes() {
163 return Collections.unmodifiableMap(this.mailboxQueue);
164 }
165
166 @Override
167 public boolean isRecursive() {
168 return !this.isSingleton;
169 }
170
171}
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..79179880
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java
@@ -0,0 +1,216 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication.timely;
10
11import java.util.Collection;
12import java.util.List;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Set;
16import java.util.function.Function;
17
18import tools.refinery.viatra.runtime.rete.itc.alg.misc.topsort.TopologicalSorting;
19import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.rete.index.IndexerListener;
22import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer;
23import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer.ListenerSubscription;
24import tools.refinery.viatra.runtime.rete.index.StandardIndexer;
25import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration;
26import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
27import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
28import tools.refinery.viatra.runtime.rete.network.Node;
29import tools.refinery.viatra.runtime.rete.network.ProductionNode;
30import tools.refinery.viatra.runtime.rete.network.StandardNode;
31import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
32import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
33import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
34import tools.refinery.viatra.runtime.rete.network.communication.NodeComparator;
35import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
36import tools.refinery.viatra.runtime.rete.single.DiscriminatorDispatcherNode;
37
38/**
39 * Timely (DDF) implementation of the {@link CommunicationTracker}.
40 *
41 * @author Tamas Szabo
42 * @since 2.3
43 */
44public class TimelyCommunicationTracker extends CommunicationTracker {
45
46 protected final TimelyConfiguration configuration;
47
48 public TimelyCommunicationTracker(final TimelyConfiguration configuration) {
49 this.configuration = configuration;
50 }
51
52 @Override
53 protected CommunicationGroup createGroup(final Node representative, final int index) {
54 final boolean isSingleton = this.sccInformationProvider.sccs.getPartition(representative).size() == 1;
55 return new TimelyCommunicationGroup(this, representative, index, isSingleton);
56 }
57
58 @Override
59 protected void reconstructQueueContents(final Set<CommunicationGroup> oldActiveGroups) {
60 for (final CommunicationGroup oldGroup : oldActiveGroups) {
61 for (final Entry<MessageSelector, Collection<Mailbox>> entry : oldGroup.getMailboxes().entrySet()) {
62 for (final Mailbox mailbox : entry.getValue()) {
63 final CommunicationGroup newGroup = this.groupMap.get(mailbox.getReceiver());
64 newGroup.notifyHasMessage(mailbox, entry.getKey());
65 }
66 }
67 }
68 }
69
70 @Override
71 public Mailbox proxifyMailbox(final Node requester, final Mailbox original) {
72 final Mailbox mailboxToProxify = (original instanceof TimelyMailboxProxy)
73 ? ((TimelyMailboxProxy) original).getWrappedMailbox()
74 : original;
75 final TimestampTransformation preprocessor = getPreprocessor(requester, mailboxToProxify.getReceiver());
76 if (preprocessor == null) {
77 return mailboxToProxify;
78 } else {
79 return new TimelyMailboxProxy(mailboxToProxify, preprocessor);
80 }
81 }
82
83 @Override
84 public IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original) {
85 final IndexerListener listenerToProxify = (original instanceof TimelyIndexerListenerProxy)
86 ? ((TimelyIndexerListenerProxy) original).getWrappedIndexerListener()
87 : original;
88 final TimestampTransformation preprocessor = getPreprocessor(requester, listenerToProxify.getOwner());
89 if (preprocessor == null) {
90 return listenerToProxify;
91 } else {
92 return new TimelyIndexerListenerProxy(listenerToProxify, preprocessor);
93 }
94 }
95
96 protected TimestampTransformation getPreprocessor(final Node source, final Node target) {
97 final Node effectiveSource = source instanceof SpecializedProjectionIndexer
98 ? ((SpecializedProjectionIndexer) source).getActiveNode()
99 : source;
100 final CommunicationGroup sourceGroup = this.getGroup(effectiveSource);
101 final CommunicationGroup targetGroup = this.getGroup(target);
102
103 if (sourceGroup != null && targetGroup != null) {
104 // during RETE construction, the groups may be still null
105 if (sourceGroup != targetGroup && sourceGroup.isRecursive()) {
106 // targetGroup is a successor SCC of sourceGroup
107 // and sourceGroup is a recursive SCC
108 // then we need to zero out the timestamps
109 return TimestampTransformation.RESET;
110 }
111 if (sourceGroup == targetGroup && target instanceof ProductionNode) {
112 // if requester and receiver are in the same SCC
113 // and receiver is a production node
114 // then we need to increment the timestamps
115 return TimestampTransformation.INCREMENT;
116 }
117 }
118
119 return null;
120 }
121
122 @Override
123 protected void postProcessNode(final Node node) {
124 if (node instanceof NetworkStructureChangeSensitiveNode) {
125 ((NetworkStructureChangeSensitiveNode) node).networkStructureChanged();
126 }
127 }
128
129 @Override
130 protected void postProcessGroup(final CommunicationGroup group) {
131 if (this.configuration.getTimelineRepresentation() == TimelineRepresentation.FAITHFUL) {
132 final Node representative = group.getRepresentative();
133 final Set<Node> groupMembers = this.sccInformationProvider.sccs.getPartition(representative);
134 if (groupMembers.size() > 1) {
135 final Graph<Node> graph = new Graph<Node>();
136
137 for (final Node node : groupMembers) {
138 graph.insertNode(node);
139 }
140
141 for (final Node source : groupMembers) {
142 for (final Node target : this.dependencyGraph.getTargetNodes(source)) {
143 // (1) the edge is not a recursion cut point
144 // (2) the edge is within this group
145 if (!this.isRecursionCutPoint(source, target) && groupMembers.contains(target)) {
146 graph.insertEdge(source, target);
147 }
148 }
149 }
150
151 final List<Node> orderedNodes = TopologicalSorting.compute(graph);
152 final Map<Node, Integer> nodeMap = CollectionsFactory.createMap();
153 int identifier = 0;
154 for (final Node orderedNode : orderedNodes) {
155 nodeMap.put(orderedNode, identifier++);
156 }
157
158 ((TimelyCommunicationGroup) group).setComparatorAndReorderMailboxes(new NodeComparator(nodeMap));
159 }
160 }
161 }
162
163 /**
164 * This static field is used for debug purposes in the DotGenerator.
165 */
166 public static final Function<Node, Function<Node, String>> EDGE_LABEL_FUNCTION = new Function<Node, Function<Node, String>>() {
167
168 @Override
169 public Function<Node, String> apply(final Node source) {
170 return new Function<Node, String>() {
171 @Override
172 public String apply(final Node target) {
173 if (source instanceof SpecializedProjectionIndexer) {
174 final Collection<ListenerSubscription> subscriptions = ((SpecializedProjectionIndexer) source)
175 .getSubscriptions();
176 for (final ListenerSubscription subscription : subscriptions) {
177 if (subscription.getListener().getOwner() == target
178 && subscription.getListener() instanceof TimelyIndexerListenerProxy) {
179 return ((TimelyIndexerListenerProxy) subscription.getListener()).preprocessor
180 .toString();
181 }
182 }
183 }
184 if (source instanceof StandardIndexer) {
185 final Collection<IndexerListener> listeners = ((StandardIndexer) source).getListeners();
186 for (final IndexerListener listener : listeners) {
187 if (listener.getOwner() == target && listener instanceof TimelyIndexerListenerProxy) {
188 return ((TimelyIndexerListenerProxy) listener).preprocessor.toString();
189 }
190 }
191 }
192 if (source instanceof StandardNode) {
193 final Collection<Mailbox> mailboxes = ((StandardNode) source).getChildMailboxes();
194 for (final Mailbox mailbox : mailboxes) {
195 if (mailbox.getReceiver() == target && mailbox instanceof TimelyMailboxProxy) {
196 return ((TimelyMailboxProxy) mailbox).preprocessor.toString();
197 }
198 }
199 }
200 if (source instanceof DiscriminatorDispatcherNode) {
201 final Collection<Mailbox> mailboxes = ((DiscriminatorDispatcherNode) source)
202 .getBucketMailboxes().values();
203 for (final Mailbox mailbox : mailboxes) {
204 if (mailbox.getReceiver() == target && mailbox instanceof TimelyMailboxProxy) {
205 return ((TimelyMailboxProxy) mailbox).preprocessor.toString();
206 }
207 }
208 }
209 return null;
210 }
211 };
212 }
213
214 };
215
216}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication.timely;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13import tools.refinery.viatra.runtime.matchers.util.Preconditions;
14import tools.refinery.viatra.runtime.rete.index.IndexerListener;
15import tools.refinery.viatra.runtime.rete.network.Node;
16import tools.refinery.viatra.runtime.rete.network.ProductionNode;
17import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
18
19/**
20 * A timely proxy for another {@link IndexerListener}, which performs some preprocessing
21 * on the differential timestamps before passing it on to the real recipient.
22 * <p>
23 * These proxies are used on edges leading into {@link ProductionNode}s. Because {@link ProductionNode}s
24 * never ask back the indexer for its contents, there is no need to also apply the proxy on that direction.
25 *
26 * @author Tamas Szabo
27 * @since 2.3
28 */
29public class TimelyIndexerListenerProxy implements IndexerListener {
30
31 protected final TimestampTransformation preprocessor;
32 protected final IndexerListener wrapped;
33
34 public TimelyIndexerListenerProxy(final IndexerListener wrapped,
35 final TimestampTransformation preprocessor) {
36 Preconditions.checkArgument(!(wrapped instanceof TimelyIndexerListenerProxy), "Proxy in a proxy is not allowed!");
37 this.wrapped = wrapped;
38 this.preprocessor = preprocessor;
39 }
40
41 public IndexerListener getWrappedIndexerListener() {
42 return wrapped;
43 }
44
45 @Override
46 public Node getOwner() {
47 return this.wrapped.getOwner();
48 }
49
50 @Override
51 public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement, final Tuple signature,
52 final boolean change, final Timestamp timestamp) {
53 this.wrapped.notifyIndexerUpdate(direction, updateElement, signature, change, preprocessor.process(timestamp));
54 }
55
56 @Override
57 public String toString() {
58 return this.preprocessor.toString() + "_PROXY -> " + this.wrapped.toString();
59 }
60
61 @Override
62 public boolean equals(final Object obj) {
63 if (obj == null || obj.getClass() != this.getClass()) {
64 return false;
65 } else if (obj == this) {
66 return true;
67 } else {
68 final TimelyIndexerListenerProxy that = (TimelyIndexerListenerProxy) obj;
69 return this.wrapped.equals(that.wrapped) && this.preprocessor == that.preprocessor;
70 }
71 }
72
73 @Override
74 public int hashCode() {
75 int hash = 1;
76 hash = hash * 17 + this.wrapped.hashCode();
77 hash = hash * 31 + this.preprocessor.hashCode();
78 return hash;
79 }
80
81}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication.timely;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13import tools.refinery.viatra.runtime.matchers.util.Preconditions;
14import tools.refinery.viatra.runtime.rete.network.Receiver;
15import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
16import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
17import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
18import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
19
20/**
21 * A timely proxy for another {@link Mailbox}, which performs some preprocessing
22 * on the differential timestamps before passing it on to the real recipient.
23 *
24 * @author Tamas Szabo
25 * @since 2.3
26 */
27public class TimelyMailboxProxy implements Mailbox {
28
29 protected final TimestampTransformation preprocessor;
30 protected final Mailbox wrapped;
31
32 public TimelyMailboxProxy(final Mailbox wrapped, final TimestampTransformation preprocessor) {
33 Preconditions.checkArgument(!(wrapped instanceof TimelyMailboxProxy), "Proxy in a proxy is not allowed!");
34 this.wrapped = wrapped;
35 this.preprocessor = preprocessor;
36 }
37
38 public Mailbox getWrappedMailbox() {
39 return wrapped;
40 }
41
42 @Override
43 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
44 this.wrapped.postMessage(direction, update, preprocessor.process(timestamp));
45 }
46
47 @Override
48 public String toString() {
49 return this.preprocessor.toString() + "_PROXY -> " + this.wrapped.toString();
50 }
51
52 @Override
53 public void clear() {
54 this.wrapped.clear();
55 }
56
57 @Override
58 public void deliverAll(final MessageSelector selector) {
59 this.wrapped.deliverAll(selector);
60 }
61
62 @Override
63 public CommunicationGroup getCurrentGroup() {
64 return this.wrapped.getCurrentGroup();
65 }
66
67 @Override
68 public void setCurrentGroup(final CommunicationGroup group) {
69 this.wrapped.setCurrentGroup(group);
70 }
71
72 @Override
73 public Receiver getReceiver() {
74 return this.wrapped.getReceiver();
75 }
76
77 @Override
78 public boolean isEmpty() {
79 return this.wrapped.isEmpty();
80 }
81
82 @Override
83 public boolean equals(final Object obj) {
84 if (obj == null || obj.getClass() != this.getClass()) {
85 return false;
86 } else if (obj == this) {
87 return true;
88 } else {
89 final TimelyMailboxProxy that = (TimelyMailboxProxy) obj;
90 return this.wrapped.equals(that.wrapped) && this.preprocessor == that.preprocessor;
91 }
92 }
93
94 @Override
95 public int hashCode() {
96 int hash = 1;
97 hash = hash * 17 + this.wrapped.hashCode();
98 hash = hash * 31 + this.preprocessor.hashCode();
99 return hash;
100 }
101
102}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.communication.timely;
10
11import tools.refinery.viatra.runtime.rete.network.Node;
12import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
13
14/**
15 * Values of this enum perform different kind of preprocessing on {@link Timestamp}s.
16 * This is used on edges leading in and out from {@link Node}s in recursive {@link TimelyCommunicationGroup}s.
17 *
18 * @author Tamas Szabo
19 * @since 2.3
20 */
21public enum TimestampTransformation {
22
23 INCREMENT {
24 @Override
25 public Timestamp process(final Timestamp timestamp) {
26 return new Timestamp(timestamp.getValue() + 1);
27 }
28
29 @Override
30 public String toString() {
31 return "INCREMENT";
32 }
33 },
34 RESET {
35 @Override
36 public Timestamp process(final Timestamp timestamp) {
37 return Timestamp.ZERO;
38 }
39
40 @Override
41 public String toString() {
42 return "RESET";
43 }
44 };
45
46 public abstract Timestamp process(final Timestamp timestamp);
47
48}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.delayed;
10
11import java.util.Collection;
12import java.util.Map;
13import java.util.Map.Entry;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.matchers.util.Signed;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.network.Network;
20import tools.refinery.viatra.runtime.rete.network.Node;
21import tools.refinery.viatra.runtime.rete.network.Receiver;
22import tools.refinery.viatra.runtime.rete.network.ReteContainer;
23import tools.refinery.viatra.runtime.rete.network.Supplier;
24import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
27
28/**
29 * Instances of this class are responsible for initializing a {@link Receiver} with the contents of a {@link Supplier}.
30 * However, due to the dynamic nature of the Rete {@link Network} and to the fact that certain {@link Node}s in the
31 * {@link Network} are sensitive to the shape of the {@link Network}, the commands must be delayed until the
32 * construction of the {@link Network} has stabilized.
33 *
34 * @author Tamas Szabo
35 * @since 2.3
36 */
37public abstract class DelayedCommand implements Runnable {
38
39 protected final Supplier supplier;
40 protected final Receiver receiver;
41 protected final Direction direction;
42 protected final ReteContainer container;
43
44 public DelayedCommand(final Supplier supplier, final Receiver receiver, final Direction direction,
45 final ReteContainer container) {
46 this.supplier = supplier;
47 this.receiver = receiver;
48 this.direction = direction;
49 this.container = container;
50 }
51
52 @Override
53 public void run() {
54 final CommunicationTracker tracker = this.container.getCommunicationTracker();
55 final Mailbox mailbox = tracker.proxifyMailbox(this.supplier, this.receiver.getMailbox());
56
57 if (this.isTimestampAware()) {
58 final Map<Tuple, Timeline<Timestamp>> contents = this.container.pullContentsWithTimeline(this.supplier,
59 false);
60 for (final Entry<Tuple, Timeline<Timestamp>> entry : contents.entrySet()) {
61 for (final Signed<Timestamp> change : entry.getValue().asChangeSequence()) {
62 mailbox.postMessage(change.getDirection().multiply(this.direction), entry.getKey(),
63 change.getPayload());
64 }
65 }
66 } else {
67 final Collection<Tuple> contents = this.container.pullContents(this.supplier, false);
68 for (final Tuple tuple : contents) {
69 mailbox.postMessage(this.direction, tuple, Timestamp.ZERO);
70 }
71 }
72 }
73
74 @Override
75 public String toString() {
76 return this.supplier + " -> " + this.receiver.toString();
77 }
78
79 protected abstract boolean isTimestampAware();
80
81}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.delayed;
10
11import tools.refinery.viatra.runtime.matchers.util.Direction;
12import tools.refinery.viatra.runtime.rete.network.Receiver;
13import tools.refinery.viatra.runtime.rete.network.ReteContainer;
14import tools.refinery.viatra.runtime.rete.network.Supplier;
15
16public class DelayedConnectCommand extends DelayedCommand {
17
18 public DelayedConnectCommand(final Supplier supplier, final Receiver receiver, final ReteContainer container) {
19 super(supplier, receiver, Direction.INSERT, container);
20 }
21
22 @Override
23 protected boolean isTimestampAware() {
24 return this.container.isTimelyEvaluation() && this.container.getCommunicationTracker().areInSameGroup(this.supplier, this.receiver);
25 }
26
27}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.delayed;
10
11import tools.refinery.viatra.runtime.matchers.util.Direction;
12import tools.refinery.viatra.runtime.rete.network.Receiver;
13import tools.refinery.viatra.runtime.rete.network.ReteContainer;
14import tools.refinery.viatra.runtime.rete.network.Supplier;
15
16public class DelayedDisconnectCommand extends DelayedCommand {
17
18 protected final boolean wasInSameSCC;
19
20 public DelayedDisconnectCommand(final Supplier supplier, final Receiver receiver, final ReteContainer container, final boolean wasInSameSCC) {
21 super(supplier, receiver, Direction.DELETE, container);
22 this.wasInSameSCC = wasInSameSCC;
23 }
24
25 @Override
26 protected boolean isTimestampAware() {
27 return this.wasInSameSCC;
28 }
29
30}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.indexer;
10
11import java.util.Collections;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
16
17/**
18 * @author Tamas Szabo
19 * @since 2.0
20 */
21public class DefaultMessageIndexer implements MessageIndexer {
22
23 protected final Map<Tuple, Integer> indexer;
24
25 public DefaultMessageIndexer() {
26 this.indexer = CollectionsFactory.createMap();
27 }
28
29 public Map<Tuple, Integer> getTuples() {
30 return Collections.unmodifiableMap(this.indexer);
31 }
32
33 @Override
34 public int getCount(final Tuple update) {
35 final Integer count = getTuples().get(update);
36 if (count == null) {
37 return 0;
38 } else {
39 return count;
40 }
41 }
42
43 @Override
44 public void insert(final Tuple update) {
45 update(update, 1);
46 }
47
48 @Override
49 public void delete(final Tuple update) {
50 update(update, -1);
51 }
52
53 @Override
54 public void update(final Tuple update, final int delta) {
55 final Integer oldCount = this.indexer.get(update);
56 final int newCount = (oldCount == null ? 0 : oldCount) + delta;
57 if (newCount == 0) {
58 this.indexer.remove(update);
59 } else {
60 this.indexer.put(update, newCount);
61 }
62 }
63
64 @Override
65 public boolean isEmpty() {
66 return this.indexer.isEmpty();
67 }
68
69 @Override
70 public void clear() {
71 this.indexer.clear();
72 }
73
74}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.indexer;
10
11import java.util.Collections;
12import java.util.Map;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
17import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
18
19/**
20 * @author Tamas Szabo
21 * @since 2.0
22 */
23public class GroupBasedMessageIndexer implements MessageIndexer {
24
25 protected final Map<Tuple, DefaultMessageIndexer> indexer;
26 protected final TupleMask groupMask;
27
28 public GroupBasedMessageIndexer(final TupleMask groupMask) {
29 this.indexer = CollectionsFactory.createMap();
30 this.groupMask = groupMask;
31 }
32
33 public Map<Tuple, Integer> getTuplesByGroup(final Tuple group) {
34 final DefaultMessageIndexer values = this.indexer.get(group);
35 if (values == null) {
36 return Collections.emptyMap();
37 } else {
38 return Collections.unmodifiableMap(values.getTuples());
39 }
40 }
41
42 @Override
43 public int getCount(final Tuple update) {
44 final Tuple group = this.groupMask.transform(update);
45 final Integer count = getTuplesByGroup(group).get(update);
46 if (count == null) {
47 return 0;
48 } else {
49 return count;
50 }
51 }
52
53 public Set<Tuple> getGroups() {
54 return Collections.unmodifiableSet(this.indexer.keySet());
55 }
56
57 @Override
58 public void insert(final Tuple update) {
59 update(update, 1);
60 }
61
62 @Override
63 public void delete(final Tuple update) {
64 update(update, -1);
65 }
66
67 @Override
68 public void update(final Tuple update, final int delta) {
69 final Tuple group = this.groupMask.transform(update);
70 DefaultMessageIndexer valueIndexer = this.indexer.get(group);
71
72 if (valueIndexer == null) {
73 valueIndexer = new DefaultMessageIndexer();
74 this.indexer.put(group, valueIndexer);
75 }
76
77 valueIndexer.update(update, delta);
78
79 // it may happen that the indexer becomes empty as a result of the update
80 if (valueIndexer.isEmpty()) {
81 this.indexer.remove(group);
82 }
83 }
84
85 @Override
86 public boolean isEmpty() {
87 return this.indexer.isEmpty();
88 }
89
90 @Override
91 public void clear() {
92 this.indexer.clear();
93 }
94
95}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.indexer;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Clearable;
13import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
14
15/**
16 * A message indexer is used by {@link Mailbox}es to index their contents.
17 *
18 * @author Tamas Szabo
19 * @since 2.0
20 */
21public interface MessageIndexer extends Clearable {
22
23 public void insert(final Tuple update);
24
25 public void delete(final Tuple update);
26
27 public void update(final Tuple update, final int delta);
28
29 public boolean isEmpty();
30
31 public int getCount(final Tuple update);
32
33}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.mailbox;
10
11import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
12import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyMailboxProxy;
13import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
14
15/**
16 * An adaptable mailbox can be wrapped by another mailbox to act in behalf of that. The significance of the adaptation
17 * is that the adaptee will notify the {@link CommunicationTracker} about updates by promoting the adapter itself.
18 * Adaptable mailboxes are used by the {@link BehaviorChangingMailbox}.
19 *
20 * Compare this with {@link TimelyMailboxProxy}. That one also wraps another mailbox in order to
21 * perform preprocessing on the messages sent to the original recipient.
22 *
23 * @author Tamas Szabo
24 * @since 2.0
25 */
26public interface AdaptableMailbox extends Mailbox {
27
28 public Mailbox getAdapter();
29
30 public void setAdapter(final Mailbox adapter);
31
32}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.mailbox;
10
11import tools.refinery.viatra.runtime.rete.network.Receiver;
12import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
13
14/**
15 * A fall through capable mailbox can directly call the update method of its {@link Receiver} instead of using the
16 * standard post-deliver mailbox semantics. If the fall through flag is set to true, the mailbox uses direct delivery,
17 * otherwise it operates in the original behavior. The fall through operation is preferable whenever applicable because
18 * it improves performance. The fall through flag is controlled by the {@link CommunicationTracker} based on the
19 * receiver node type and network topology.
20 *
21 * @author Tamas Szabo
22 * @since 2.2
23 */
24public interface FallThroughCapableMailbox extends Mailbox {
25
26 public boolean isFallThrough();
27
28 public void setFallThrough(final boolean fallThrough);
29
30}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.mailbox;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Clearable;
13import tools.refinery.viatra.runtime.matchers.util.Direction;
14import tools.refinery.viatra.runtime.rete.network.IGroupable;
15import tools.refinery.viatra.runtime.rete.network.Receiver;
16import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
17import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
18import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
19
20/**
21 * A mailbox is associated with every {@link Receiver}. Messages can be sent to a {@link Receiver} by posting them into
22 * the mailbox. Different mailbox implementations may differ in the way how they deliver the posted messages.
23 *
24 * @author Tamas Szabo
25 * @since 2.0
26 *
27 */
28public interface Mailbox extends Clearable, IGroupable {
29
30 /**
31 * Posts a new message to this mailbox.
32 *
33 * @param direction
34 * the direction of the update
35 * @param update
36 * the update element
37 * @since 2.4
38 */
39 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp);
40
41 /**
42 * Delivers all messages according to the given selector from this mailbox. The selector can also be null. In this case, no
43 * special separation is expected between the messages.
44 *
45 * @param selector the message selector
46 */
47 public void deliverAll(final MessageSelector selector);
48
49 /**
50 * Returns the {@link Receiver} of this mailbox.
51 *
52 * @return the receiver
53 */
54 public Receiver getReceiver();
55
56 /**
57 * Returns the {@link CommunicationGroup} of the receiver of this mailbox.
58 *
59 * @return the communication group
60 */
61 public CommunicationGroup getCurrentGroup();
62
63 /**
64 * Sets the {@link CommunicationGroup} that the receiver of this mailbox is associated with.
65 *
66 * @param group
67 * the communication group
68 */
69 public void setCurrentGroup(final CommunicationGroup group);
70
71 /**
72 * Returns true if this mailbox is empty.
73 *
74 * @return
75 */
76 public boolean isEmpty();
77
78}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.mailbox;
10
11import tools.refinery.viatra.runtime.rete.network.indexer.MessageIndexer;
12
13/**
14 * A factory used to create message indexers for {@link Mailbox}es.
15 *
16 * @author Tamas Szabo
17 * @since 2.0
18 */
19public interface MessageIndexerFactory<I extends MessageIndexer> {
20
21 public I create();
22
23}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.mailbox.timeless;
10
11import tools.refinery.viatra.runtime.rete.network.Receiver;
12import tools.refinery.viatra.runtime.rete.network.ReteContainer;
13import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
14import tools.refinery.viatra.runtime.rete.network.indexer.MessageIndexer;
15import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
16import tools.refinery.viatra.runtime.rete.network.mailbox.MessageIndexerFactory;
17
18/**
19 * An abstract mailbox implementation that is capable of splitting update messages based on some form of monotonicity
20 * (anti-monotone and monotone). The monotonicity is either defined by the less or equal operator of a poset or, it can
21 * be the standard subset ordering among sets of tuples.
22 *
23 * @author Tamas Szabo
24 * @since 2.0
25 *
26 */
27public abstract class AbstractUpdateSplittingMailbox<IndexerType extends MessageIndexer, ReceiverType extends Receiver> implements Mailbox {
28
29 protected IndexerType monotoneQueue;
30 protected IndexerType antiMonotoneQueue;
31 protected IndexerType monotoneBuffer;
32 protected IndexerType antiMonotoneBuffer;
33 protected boolean deliveringMonotone;
34 protected boolean deliveringAntiMonotone;
35 protected final ReceiverType receiver;
36 protected final ReteContainer container;
37 protected CommunicationGroup group;
38
39 public AbstractUpdateSplittingMailbox(final ReceiverType receiver, final ReteContainer container,
40 final MessageIndexerFactory<IndexerType> factory) {
41 this.receiver = receiver;
42 this.container = container;
43 this.monotoneQueue = factory.create();
44 this.antiMonotoneQueue = factory.create();
45 this.monotoneBuffer = factory.create();
46 this.antiMonotoneBuffer = factory.create();
47 this.deliveringMonotone = false;
48 this.deliveringAntiMonotone = false;
49 }
50
51 protected void swapAndClearMonotone() {
52 final IndexerType tmp = this.monotoneQueue;
53 this.monotoneQueue = this.monotoneBuffer;
54 this.monotoneBuffer = tmp;
55 this.monotoneBuffer.clear();
56 }
57
58 protected void swapAndClearAntiMonotone() {
59 final IndexerType tmp = this.antiMonotoneQueue;
60 this.antiMonotoneQueue = this.antiMonotoneBuffer;
61 this.antiMonotoneBuffer = tmp;
62 this.antiMonotoneBuffer.clear();
63 }
64
65 protected IndexerType getActiveMonotoneQueue() {
66 if (this.deliveringMonotone) {
67 return this.monotoneBuffer;
68 } else {
69 return this.monotoneQueue;
70 }
71 }
72
73 protected IndexerType getActiveAntiMonotoneQueue() {
74 if (this.deliveringAntiMonotone) {
75 return this.antiMonotoneBuffer;
76 } else {
77 return this.antiMonotoneQueue;
78 }
79 }
80
81 @Override
82 public ReceiverType getReceiver() {
83 return this.receiver;
84 }
85
86 @Override
87 public void clear() {
88 this.monotoneQueue.clear();
89 this.antiMonotoneQueue.clear();
90 this.monotoneBuffer.clear();
91 this.antiMonotoneBuffer.clear();
92 }
93
94 @Override
95 public boolean isEmpty() {
96 return this.getActiveMonotoneQueue().isEmpty() && this.getActiveAntiMonotoneQueue().isEmpty();
97 }
98
99 @Override
100 public CommunicationGroup getCurrentGroup() {
101 return this.group;
102 }
103
104 @Override
105 public void setCurrentGroup(final CommunicationGroup group) {
106 this.group = group;
107 }
108
109}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.mailbox.timeless;
10
11import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
12import tools.refinery.viatra.runtime.matchers.util.Direction;
13import tools.refinery.viatra.runtime.rete.network.Node;
14import tools.refinery.viatra.runtime.rete.network.Receiver;
15import tools.refinery.viatra.runtime.rete.network.ReteContainer;
16import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
17import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
18import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20import tools.refinery.viatra.runtime.rete.network.communication.timeless.TimelessCommunicationTracker;
21import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox;
22import tools.refinery.viatra.runtime.rete.network.mailbox.FallThroughCapableMailbox;
23
24/**
25 * This mailbox changes its behavior based on the position of its {@link Receiver} in the network topology.
26 * It either behaves as a {@link DefaultMailbox} or as an {@link UpdateSplittingMailbox}. The decision is made by the
27 * {@link CommunicationTracker}, see {@link TimelessCommunicationTracker#postProcessNode(Node)} for more details.
28 *
29 * @author Tamas Szabo
30 */
31public class BehaviorChangingMailbox implements FallThroughCapableMailbox {
32
33 protected boolean fallThrough;
34 protected boolean split;
35 protected AdaptableMailbox wrapped;
36 protected final Receiver receiver;
37 protected final ReteContainer container;
38 protected CommunicationGroup group;
39
40 public BehaviorChangingMailbox(final Receiver receiver, final ReteContainer container) {
41 this.fallThrough = false;
42 this.split = false;
43 this.receiver = receiver;
44 this.container = container;
45 this.wrapped = new DefaultMailbox(receiver, container);
46 this.wrapped.setAdapter(this);
47 }
48
49 @Override
50 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
51 if (this.fallThrough && !this.container.isExecutingDelayedCommands()) {
52 // disable fall through while we are in the middle of executing delayed construction commands
53 this.receiver.update(direction, update, timestamp);
54 } else {
55 this.wrapped.postMessage(direction, update, timestamp);
56 }
57 }
58
59 @Override
60 public void deliverAll(final MessageSelector kind) {
61 this.wrapped.deliverAll(kind);
62 }
63
64 @Override
65 public String toString() {
66 return "A_MBOX -> " + this.wrapped;
67 }
68
69 public void setSplitFlag(final boolean splitValue) {
70 if (this.split != splitValue) {
71 assert isEmpty();
72 if (splitValue) {
73 this.wrapped = new UpdateSplittingMailbox(this.receiver, this.container);
74 } else {
75 this.wrapped = new DefaultMailbox(this.receiver, this.container);
76 }
77 this.wrapped.setAdapter(this);
78 this.split = splitValue;
79 }
80 }
81
82 @Override
83 public boolean isEmpty() {
84 return this.wrapped.isEmpty();
85 }
86
87 @Override
88 public void clear() {
89 this.wrapped.clear();
90 }
91
92 @Override
93 public Receiver getReceiver() {
94 return this.receiver;
95 }
96
97 @Override
98 public CommunicationGroup getCurrentGroup() {
99 return this.group;
100 }
101
102 @Override
103 public void setCurrentGroup(final CommunicationGroup group) {
104 this.group = group;
105 }
106
107 @Override
108 public boolean isFallThrough() {
109 return this.fallThrough;
110 }
111
112 @Override
113 public void setFallThrough(final boolean fallThrough) {
114 this.fallThrough = fallThrough;
115 }
116
117} \ 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..baf7270f
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java
@@ -0,0 +1,163 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.mailbox.timeless;
10
11import java.util.Map;
12
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
15import tools.refinery.viatra.runtime.matchers.util.Direction;
16import tools.refinery.viatra.runtime.rete.network.Receiver;
17import tools.refinery.viatra.runtime.rete.network.ReteContainer;
18import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
19import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
20import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector;
21import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
22import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox;
23import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
24
25/**
26 * Default mailbox implementation.
27 * <p>
28 * Usually, the mailbox performs counting of messages so that they can cancel each other out. However, if marked as a
29 * fall-through mailbox, than update messages are delivered directly to the receiver node to reduce overhead.
30 *
31 * @author Tamas Szabo
32 * @since 2.0
33 */
34public class DefaultMailbox implements AdaptableMailbox {
35
36 private static int SIZE_TRESHOLD = 127;
37
38 protected Map<Tuple, Integer> queue;
39 protected Map<Tuple, Integer> buffer;
40 protected final Receiver receiver;
41 protected final ReteContainer container;
42 protected boolean delivering;
43 protected Mailbox adapter;
44 protected CommunicationGroup group;
45
46 public DefaultMailbox(final Receiver receiver, final ReteContainer container) {
47 this.receiver = receiver;
48 this.container = container;
49 this.queue = CollectionsFactory.createMap();
50 this.buffer = CollectionsFactory.createMap();
51 this.adapter = this;
52 }
53
54 protected Map<Tuple, Integer> getActiveQueue() {
55 if (this.delivering) {
56 return this.buffer;
57 } else {
58 return this.queue;
59 }
60 }
61
62 @Override
63 public Mailbox getAdapter() {
64 return this.adapter;
65 }
66
67 @Override
68 public void setAdapter(final Mailbox adapter) {
69 this.adapter = adapter;
70 }
71
72 @Override
73 public boolean isEmpty() {
74 return getActiveQueue().isEmpty();
75 }
76
77 @Override
78 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
79 final Map<Tuple, Integer> activeQueue = getActiveQueue();
80 final boolean wasEmpty = activeQueue.isEmpty();
81
82 boolean significantChange = false;
83 Integer count = activeQueue.get(update);
84 if (count == null) {
85 count = 0;
86 significantChange = true;
87 }
88
89 if (direction == Direction.DELETE) {
90 count--;
91 } else {
92 count++;
93 }
94
95 if (count == 0) {
96 activeQueue.remove(update);
97 significantChange = true;
98 } else {
99 activeQueue.put(update, count);
100 }
101
102 if (significantChange) {
103 final Mailbox targetMailbox = this.adapter;
104 final CommunicationGroup targetGroup = this.adapter.getCurrentGroup();
105
106 if (wasEmpty) {
107 targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.DEFAULT);
108 } else if (activeQueue.isEmpty()) {
109 targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.DEFAULT);
110 }
111 }
112 }
113
114 @Override
115 public void deliverAll(final MessageSelector kind) {
116 if (kind == PhasedSelector.DEFAULT) {
117 // use the buffer during delivering so that there is a clear
118 // separation between the stages
119 this.delivering = true;
120 this.receiver.batchUpdate(this.queue.entrySet(), Timestamp.ZERO);
121 this.delivering = false;
122
123 if (queue.size() > SIZE_TRESHOLD) {
124 this.queue = this.buffer;
125 this.buffer = CollectionsFactory.createMap();
126 } else {
127 this.queue.clear();
128 final Map<Tuple, Integer> tmpQueue = this.queue;
129 this.queue = this.buffer;
130 this.buffer = tmpQueue;
131 }
132 } else {
133 throw new IllegalArgumentException("Unsupported message kind " + kind);
134 }
135 }
136
137 @Override
138 public String toString() {
139 return "D_MBOX (" + this.receiver + ") " + this.getActiveQueue();
140 }
141
142 @Override
143 public Receiver getReceiver() {
144 return this.receiver;
145 }
146
147 @Override
148 public void clear() {
149 this.queue.clear();
150 this.buffer.clear();
151 }
152
153 @Override
154 public CommunicationGroup getCurrentGroup() {
155 return this.group;
156 }
157
158 @Override
159 public void setCurrentGroup(final CommunicationGroup group) {
160 this.group = group;
161 }
162
163}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.mailbox.timeless;
10
11import java.util.HashSet;
12import java.util.Map.Entry;
13import java.util.Set;
14
15import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
23import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector;
24import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
25import tools.refinery.viatra.runtime.rete.network.indexer.GroupBasedMessageIndexer;
26
27/**
28 * A monotonicity aware mailbox implementation. The mailbox uses an {@link IPosetComparator} to identify if a pair of
29 * REVOKE - INSERT updates represent a monotone change pair. The mailbox is used by {@link PosetAwareReceiver}s.
30 *
31 * @author Tamas Szabo
32 * @since 2.0
33 */
34public class PosetAwareMailbox extends AbstractUpdateSplittingMailbox<GroupBasedMessageIndexer, PosetAwareReceiver> {
35
36 protected final TupleMask groupMask;
37
38 public PosetAwareMailbox(final PosetAwareReceiver receiver, final ReteContainer container) {
39 super(receiver, container, () -> new GroupBasedMessageIndexer(receiver.getCoreMask()));
40 this.groupMask = receiver.getCoreMask();
41 }
42
43 @Override
44 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
45 final GroupBasedMessageIndexer monotoneQueue = getActiveMonotoneQueue();
46 final GroupBasedMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue();
47 final boolean wasPresentAsMonotone = monotoneQueue.getCount(update) != 0;
48 final boolean wasPresentAsAntiMonotone = antiMonotoneQueue.getCount(update) != 0;
49 final TupleMask coreMask = this.receiver.getCoreMask();
50
51 // it cannot happen that it was present in both
52 assert !(wasPresentAsMonotone && wasPresentAsAntiMonotone);
53
54 if (direction == Direction.INSERT) {
55 if (wasPresentAsAntiMonotone) {
56 // it was an anti-monotone one before
57 antiMonotoneQueue.insert(update);
58 } else {
59 // it was a monotone one before or did not exist at all
60 monotoneQueue.insert(update);
61
62 // if it was not present in the monotone queue before, then
63 // we need to check whether it makes REVOKE updates monotone
64 if (!wasPresentAsMonotone) {
65 final Set<Tuple> counterParts = tryFindCounterPart(update, false, true);
66 for (final Tuple counterPart : counterParts) {
67 final int count = antiMonotoneQueue.getCount(counterPart);
68 assert count < 0;
69 antiMonotoneQueue.update(counterPart, -count);
70 monotoneQueue.update(counterPart, count);
71 }
72 }
73 }
74 } else {
75 if (wasPresentAsAntiMonotone) {
76 // it was an anti-monotone one before
77 antiMonotoneQueue.delete(update);
78 } else if (wasPresentAsMonotone) {
79 // it was a monotone one before
80 monotoneQueue.delete(update);
81
82 // and we need to check whether the monotone REVOKE updates
83 // still have a reinforcing counterpart
84 final Set<Tuple> candidates = new HashSet<Tuple>();
85 final Tuple key = coreMask.transform(update);
86 for (final Entry<Tuple, Integer> entry : monotoneQueue.getTuplesByGroup(key).entrySet()) {
87 if (entry.getValue() < 0) {
88 final Tuple candidate = entry.getKey();
89 final Set<Tuple> counterParts = tryFindCounterPart(candidate, true, false);
90 if (counterParts.isEmpty()) {
91 // all of them are gone
92 candidates.add(candidate);
93 }
94 }
95 }
96
97 // move the candidates from the monotone queue to the
98 // anti-monotone queue because they do not have a
99 // counterpart anymore
100 for (final Tuple candidate : candidates) {
101 final int count = monotoneQueue.getCount(candidate);
102 assert count < 0;
103 monotoneQueue.update(candidate, -count);
104 antiMonotoneQueue.update(candidate, count);
105 }
106 } else {
107 // it did not exist before
108 final Set<Tuple> counterParts = tryFindCounterPart(update, true, false);
109 if (counterParts.isEmpty()) {
110 // there is no tuple that would make this update monotone
111 antiMonotoneQueue.delete(update);
112 } else {
113 // there is a reinforcing counterpart
114 monotoneQueue.delete(update);
115 }
116 }
117 }
118
119 if (antiMonotoneQueue.isEmpty()) {
120 this.group.notifyLostAllMessages(this, PhasedSelector.ANTI_MONOTONE);
121 } else {
122 this.group.notifyHasMessage(this, PhasedSelector.ANTI_MONOTONE);
123 }
124
125 if (monotoneQueue.isEmpty()) {
126 this.group.notifyLostAllMessages(this, PhasedSelector.MONOTONE);
127 } else {
128 this.group.notifyHasMessage(this, PhasedSelector.MONOTONE);
129 }
130 }
131
132 protected Set<Tuple> tryFindCounterPart(final Tuple first, final boolean findPositiveCounterPart,
133 final boolean findAllCounterParts) {
134 final GroupBasedMessageIndexer monotoneQueue = getActiveMonotoneQueue();
135 final GroupBasedMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue();
136 final TupleMask coreMask = this.receiver.getCoreMask();
137 final TupleMask posetMask = this.receiver.getPosetMask();
138 final IPosetComparator posetComparator = this.receiver.getPosetComparator();
139 final Set<Tuple> result = CollectionsFactory.createSet();
140 final Tuple firstKey = coreMask.transform(first);
141 final Tuple firstValue = posetMask.transform(first);
142
143 if (findPositiveCounterPart) {
144 for (final Entry<Tuple, Integer> entry : monotoneQueue.getTuplesByGroup(firstKey).entrySet()) {
145 final Tuple secondValue = posetMask.transform(entry.getKey());
146 if (entry.getValue() > 0 && posetComparator.isLessOrEqual(firstValue, secondValue)) {
147 result.add(entry.getKey());
148 if (!findAllCounterParts) {
149 return result;
150 }
151 }
152 }
153 } else {
154 for (final Entry<Tuple, Integer> entry : antiMonotoneQueue.getTuplesByGroup(firstKey).entrySet()) {
155 final Tuple secondValue = posetMask.transform(entry.getKey());
156 if (posetComparator.isLessOrEqual(secondValue, firstValue)) {
157 result.add(entry.getKey());
158 if (!findAllCounterParts) {
159 return result;
160 }
161 }
162 }
163 }
164
165 return result;
166 }
167
168 @Override
169 public void deliverAll(final MessageSelector kind) {
170 if (kind == PhasedSelector.ANTI_MONOTONE) {
171 // use the buffer during delivering so that there is a clear
172 // separation between the stages
173 this.deliveringAntiMonotone = true;
174
175 for (final Tuple group : this.antiMonotoneQueue.getGroups()) {
176 for (final Entry<Tuple, Integer> entry : this.antiMonotoneQueue.getTuplesByGroup(group).entrySet()) {
177 final Tuple update = entry.getKey();
178 final int count = entry.getValue();
179 assert count < 0;
180 for (int i = 0; i < Math.abs(count); i++) {
181 this.receiver.updateWithPosetInfo(Direction.DELETE, update, false);
182 }
183 }
184 }
185
186 this.deliveringAntiMonotone = false;
187 swapAndClearAntiMonotone();
188 } else if (kind == PhasedSelector.MONOTONE) {
189 // use the buffer during delivering so that there is a clear
190 // separation between the stages
191 this.deliveringMonotone = true;
192
193 for (final Tuple group : this.monotoneQueue.getGroups()) {
194 for (final Entry<Tuple, Integer> entry : this.monotoneQueue.getTuplesByGroup(group).entrySet()) {
195 final Tuple update = entry.getKey();
196 final int count = entry.getValue();
197 assert count != 0;
198 final Direction direction = count < 0 ? Direction.DELETE : Direction.INSERT;
199 for (int i = 0; i < Math.abs(count); i++) {
200 this.receiver.updateWithPosetInfo(direction, update, true);
201 }
202 }
203 }
204
205 this.deliveringMonotone = false;
206 swapAndClearMonotone();
207 } else {
208 throw new IllegalArgumentException("Unsupported message kind " + kind);
209 }
210 }
211
212 @Override
213 public String toString() {
214 return "PA_MBOX (" + this.receiver + ") " + this.getActiveMonotoneQueue() + " "
215 + this.getActiveAntiMonotoneQueue();
216 }
217
218}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.mailbox.timeless;
10
11import java.util.Map.Entry;
12
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.util.Direction;
15import tools.refinery.viatra.runtime.rete.network.Receiver;
16import tools.refinery.viatra.runtime.rete.network.ReteContainer;
17import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
18import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
19import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21import tools.refinery.viatra.runtime.rete.network.indexer.DefaultMessageIndexer;
22import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox;
23import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
24
25/**
26 * A mailbox implementation that splits updates messages according to the standard subset ordering into anti-monotonic
27 * (deletions) and monotonic (insertions) updates.
28 *
29 * @author Tamas Szabo
30 * @since 2.0
31 */
32public class UpdateSplittingMailbox extends AbstractUpdateSplittingMailbox<DefaultMessageIndexer, Receiver>
33 implements AdaptableMailbox {
34
35 protected Mailbox adapter;
36
37 public UpdateSplittingMailbox(final Receiver receiver, final ReteContainer container) {
38 super(receiver, container, DefaultMessageIndexer::new);
39 this.adapter = this;
40 }
41
42 @Override
43 public Mailbox getAdapter() {
44 return this.adapter;
45 }
46
47 @Override
48 public void setAdapter(final Mailbox adapter) {
49 this.adapter = adapter;
50 }
51
52 @Override
53 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
54 final DefaultMessageIndexer monotoneQueue = getActiveMonotoneQueue();
55 final DefaultMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue();
56 final boolean wasPresentAsMonotone = monotoneQueue.getCount(update) != 0;
57 final boolean wasPresentAsAntiMonotone = antiMonotoneQueue.getCount(update) != 0;
58
59 // it cannot happen that it was present in both
60 assert !(wasPresentAsMonotone && wasPresentAsAntiMonotone);
61
62 if (direction == Direction.INSERT) {
63 if (wasPresentAsAntiMonotone) {
64 // it was an anti-monotone one before
65 antiMonotoneQueue.insert(update);
66 } else {
67 // it was a monotone one before or did not exist at all
68 monotoneQueue.insert(update);
69 }
70 } else {
71 if (wasPresentAsMonotone) {
72 // it was a monotone one before
73 monotoneQueue.delete(update);
74 } else {
75 // it was an anti-monotone one before or did not exist at all
76 antiMonotoneQueue.delete(update);
77 }
78 }
79
80 final Mailbox targetMailbox = this.adapter;
81 final CommunicationGroup targetGroup = this.adapter.getCurrentGroup();
82
83 if (antiMonotoneQueue.isEmpty()) {
84 targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.ANTI_MONOTONE);
85 } else {
86 targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.ANTI_MONOTONE);
87 }
88
89 if (monotoneQueue.isEmpty()) {
90 targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.MONOTONE);
91 } else {
92 targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.MONOTONE);
93 }
94 }
95
96 @Override
97 public void deliverAll(final MessageSelector kind) {
98 if (kind == PhasedSelector.ANTI_MONOTONE) {
99 // deliver anti-monotone
100 this.deliveringAntiMonotone = true;
101 for (final Entry<Tuple, Integer> entry : this.antiMonotoneQueue.getTuples().entrySet()) {
102 final Tuple update = entry.getKey();
103 final int count = entry.getValue();
104 assert count < 0;
105 for (int i = 0; i < Math.abs(count); i++) {
106 this.receiver.update(Direction.DELETE, update, Timestamp.ZERO);
107 }
108 }
109 this.deliveringAntiMonotone = false;
110 swapAndClearAntiMonotone();
111 } else if (kind == PhasedSelector.MONOTONE) {
112 // deliver monotone
113 this.deliveringMonotone = true;
114 for (final Entry<Tuple, Integer> entry : this.monotoneQueue.getTuples().entrySet()) {
115 final Tuple update = entry.getKey();
116 final int count = entry.getValue();
117 assert count > 0;
118 for (int i = 0; i < count; i++) {
119 this.receiver.update(Direction.INSERT, update, Timestamp.ZERO);
120 }
121 }
122 this.deliveringMonotone = false;
123 swapAndClearMonotone();
124 } else {
125 throw new IllegalArgumentException("Unsupported message kind " + kind);
126 }
127 }
128
129 @Override
130 public String toString() {
131 return "US_MBOX (" + this.receiver + ") " + this.getActiveMonotoneQueue() + " "
132 + this.getActiveAntiMonotoneQueue();
133 }
134
135}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.network.mailbox.timely;
10
11import java.util.Map;
12import java.util.TreeMap;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
18import tools.refinery.viatra.runtime.rete.network.Receiver;
19import tools.refinery.viatra.runtime.rete.network.ReteContainer;
20import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
21import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
24import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
25
26public class TimelyMailbox implements Mailbox {
27
28 protected TreeMap<Timestamp, Map<Tuple, Integer>> queue;
29 protected final Receiver receiver;
30 protected final ReteContainer container;
31 protected CommunicationGroup group;
32 protected boolean fallThrough;
33
34 public TimelyMailbox(final Receiver receiver, final ReteContainer container) {
35 this.receiver = receiver;
36 this.container = container;
37 this.queue = CollectionsFactory.createTreeMap();
38 }
39
40 protected TreeMap<Timestamp, Map<Tuple, Integer>> getActiveQueue() {
41 return this.queue;
42 }
43
44 @Override
45 public boolean isEmpty() {
46 return getActiveQueue().isEmpty();
47 }
48
49 @Override
50 public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) {
51 final TreeMap<Timestamp, Map<Tuple, Integer>> activeQueue = getActiveQueue();
52
53 Map<Tuple, Integer> tupleMap = activeQueue.get(timestamp);
54 final boolean wasEmpty = tupleMap == null;
55 boolean significantChange = false;
56
57 if (tupleMap == null) {
58 tupleMap = CollectionsFactory.createMap();
59 activeQueue.put(timestamp, tupleMap);
60 significantChange = true;
61 }
62
63 Integer count = tupleMap.get(update);
64 if (count == null) {
65 count = 0;
66 significantChange = true;
67 }
68
69 if (direction == Direction.DELETE) {
70 count--;
71 } else {
72 count++;
73 }
74
75 if (count == 0) {
76 tupleMap.remove(update);
77 if (tupleMap.isEmpty()) {
78 activeQueue.remove(timestamp);
79 }
80 significantChange = true;
81 } else {
82 tupleMap.put(update, count);
83 }
84
85 if (significantChange) {
86 if (wasEmpty) {
87 this.group.notifyHasMessage(this, timestamp);
88 } else if (tupleMap.isEmpty()) {
89 final Timestamp resumableTimestamp = (this.receiver instanceof ResumableNode)
90 ? ((ResumableNode) this.receiver).getResumableTimestamp()
91 : null;
92 // check if there is folding left to do before unsubscribing just based on the message queue being empty
93 if (resumableTimestamp == null || resumableTimestamp.compareTo(timestamp) != 0) {
94 this.group.notifyLostAllMessages(this, timestamp);
95 }
96 }
97 }
98 }
99
100 @Override
101 public void deliverAll(final MessageSelector selector) {
102 if (selector instanceof Timestamp) {
103 final Timestamp timestamp = (Timestamp) selector;
104 // REMOVE the tuples associated with the selector, dont just query them
105 final Map<Tuple, Integer> tupleMap = this.queue.remove(timestamp);
106
107 // tupleMap may be empty if we only have lazy folding to do
108 if (tupleMap != null) {
109 this.receiver.batchUpdate(tupleMap.entrySet(), timestamp);
110 }
111
112 if (this.container.getTimelyConfiguration()
113 .getTimelineRepresentation() == TimelineRepresentation.FAITHFUL) {
114 // (1) either normal delivery, which ended up being a lazy folding state
115 // (2) and/or lazy folding needs to be resumed
116 if (this.receiver instanceof ResumableNode) {
117 ((ResumableNode) this.receiver).resumeAt(timestamp);
118 }
119 }
120 } else {
121 throw new IllegalArgumentException("Unsupported message selector " + selector);
122 }
123 }
124
125 @Override
126 public String toString() {
127 return "DDF_MBOX (" + this.receiver + ") " + this.getActiveQueue();
128 }
129
130 @Override
131 public Receiver getReceiver() {
132 return this.receiver;
133 }
134
135 @Override
136 public void clear() {
137 this.queue.clear();
138 }
139
140 @Override
141 public CommunicationGroup getCurrentGroup() {
142 return this.group;
143 }
144
145 @Override
146 public void setCurrentGroup(final CommunicationGroup group) {
147 this.group = group;
148 }
149
150}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.remote;
11
12import tools.refinery.viatra.runtime.rete.network.Node;
13import tools.refinery.viatra.runtime.rete.network.ReteContainer;
14
15/**
16 * Remote identifier of a node of type T.
17 *
18 * @author Gabor Bergmann
19 *
20 */
21public class Address<T extends Node> {
22 ReteContainer container;
23 Long nodeId;
24 /**
25 * Feel free to leave null e.g. if node is in a separate JVM.
26 */
27 T nodeCache;
28
29 /**
30 * Address of local node (use only for containers in the same VM!)
31 */
32 public static <N extends Node> Address<N> of(N node) {
33 return new Address<N>(node);
34 }
35
36 /**
37 * General constructor.
38 *
39 * @param container
40 * @param nodeId
41 */
42 public Address(ReteContainer container, Long nodeId) {
43 super();
44 this.container = container;
45 this.nodeId = nodeId;
46 }
47
48 /**
49 * Local-only constructor. (use only for containers in the same VM!)
50 *
51 * @param node
52 * the node to address
53 */
54 public Address(T node) {
55 super();
56 this.nodeCache = node;
57 this.container = node.getContainer();
58 this.nodeId = node.getNodeId();
59 }
60
61 @Override
62 public int hashCode() {
63 final int prime = 31;
64 int result = 1;
65 result = prime * result + ((container == null) ? 0 : container.hashCode());
66 result = prime * result + ((nodeId == null) ? 0 : nodeId.hashCode());
67 return result;
68 }
69
70 @Override
71 public boolean equals(Object obj) {
72 if (this == obj)
73 return true;
74 if (obj == null)
75 return false;
76 if (!(obj instanceof Address<?>))
77 return false;
78 final Address<?> other = (Address<?>) obj;
79 if (container == null) {
80 if (other.container != null)
81 return false;
82 } else if (!container.equals(other.container))
83 return false;
84 if (nodeId == null) {
85 if (other.nodeId != null)
86 return false;
87 } else if (!nodeId.equals(other.nodeId))
88 return false;
89 return true;
90 }
91
92 public ReteContainer getContainer() {
93 return container;
94 }
95
96 public void setContainer(ReteContainer container) {
97 this.container = container;
98 }
99
100 public Long getNodeId() {
101 return nodeId;
102 }
103
104 public void setNodeId(Long nodeId) {
105 this.nodeId = nodeId;
106 }
107
108 public T getNodeCache() {
109 return nodeCache;
110 }
111
112 public void setNodeCache(T nodeCache) {
113 this.nodeCache = nodeCache;
114 }
115
116 @Override
117 public String toString() {
118 if (nodeCache == null)
119 return "A(" + nodeId + " @ " + container + ")";
120 else
121 return "A(" + nodeCache + ")";
122
123 }
124
125}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.remote;
11
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.List;
15import java.util.Map;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
20import tools.refinery.viatra.runtime.rete.network.Receiver;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
24
25/**
26 * This node delivers updates to a remote recipient; no updates are propagated further in this network.
27 *
28 * @author Gabor Bergmann
29 *
30 */
31public class RemoteReceiver extends SingleInputNode {
32
33 List<Address<? extends Receiver>> targets;
34
35 public RemoteReceiver(ReteContainer reteContainer) {
36 super(reteContainer);
37 targets = new ArrayList<Address<? extends Receiver>>();
38 }
39
40 public void addTarget(Address<? extends Receiver> target) {
41 targets.add(target);
42 }
43
44 @Override
45 public void pullInto(Collection<Tuple> collector, boolean flush) {
46 propagatePullInto(collector, flush);
47 }
48
49 @Override
50 public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) {
51 throw new UnsupportedOperationException();
52 }
53
54 public Collection<Tuple> remotePull(boolean flush) {
55 return reteContainer.pullContents(this, flush);
56 }
57
58 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
59 for (Address<? extends Receiver> ad : targets)
60 reteContainer.sendUpdateToRemoteAddress(ad, direction, updateElement);
61 }
62
63}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.remote;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
21
22/**
23 * This node receives updates from a remote supplier; no local updates are expected.
24 *
25 * @author Gabor Bergmann
26 *
27 */
28public class RemoteSupplier extends SingleInputNode {
29
30 RemoteReceiver counterpart;
31
32 public RemoteSupplier(ReteContainer reteContainer, RemoteReceiver counterpart) {
33 super(reteContainer);
34 this.counterpart = counterpart;
35 counterpart.addTarget(reteContainer.makeAddress(this));
36 }
37
38 @Override
39 public void pullInto(Collection<Tuple> collector, boolean flush) {
40 Collection<Tuple> pulled = counterpart.remotePull(flush);
41 collector.addAll(pulled);
42 }
43
44 @Override
45 public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) {
46 throw new UnsupportedOperationException();
47 }
48
49 @Override
50 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
51 propagateUpdate(direction, updateElement, timestamp);
52 }
53
54}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.single;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.List;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
20import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer.ListenerSubscription;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.StandardNode;
23import tools.refinery.viatra.runtime.rete.network.Supplier;
24import tools.refinery.viatra.runtime.rete.network.Tunnel;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
27import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
28import tools.refinery.viatra.runtime.rete.util.Options;
29
30/**
31 * Ensures that no identical copies get to the output. Only one replica of each pattern substitution may traverse this
32 * node. There are both timeless and timely implementations.
33 *
34 * @author Gabor Bergmann
35 * @author Tamas Szabo
36 * @noinstantiate This class is not intended to be instantiated by clients.
37 * @noextend This class is not intended to be subclassed by clients.
38 * @since 2.2
39 */
40public abstract class AbstractUniquenessEnforcerNode extends StandardNode implements Tunnel {
41
42 protected final Collection<Supplier> parents;
43 protected ProjectionIndexer memoryNullIndexer;
44 protected ProjectionIndexer memoryIdentityIndexer;
45 protected final int tupleWidth;
46 // MUST BE INSTANTIATED IN THE CONCRETE SUBCLASSES AFTER ALL FIELDS ARE SET
47 protected Mailbox mailbox;
48 protected final TupleMask nullMask;
49 protected final TupleMask identityMask;
50 protected final List<ListenerSubscription> specializedListeners;
51
52 public AbstractUniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth) {
53 super(reteContainer);
54 this.parents = new ArrayList<Supplier>();
55 this.specializedListeners = new ArrayList<ListenerSubscription>();
56 this.tupleWidth = tupleWidth;
57 this.nullMask = TupleMask.linear(0, tupleWidth);
58 this.identityMask = TupleMask.identity(tupleWidth);
59 }
60
61 protected abstract Mailbox instantiateMailbox();
62
63 @Override
64 public Mailbox getMailbox() {
65 return this.mailbox;
66 }
67
68 /**
69 * @since 2.8
70 */
71 public abstract Set<Tuple> getTuples();
72
73 /**
74 * @since 2.4
75 */
76 protected void propagate(final Direction direction, final Tuple update, final Timestamp timestamp) {
77 // See Bug 518434
78 // trivial (non-active) indexers must be updated before other listeners
79 // so that if they are joined against each other, trivial indexers lookups
80 // will be consistent with their notifications;
81 // also, their subscriptions must share a single order
82 for (final ListenerSubscription subscription : specializedListeners) {
83 subscription.propagate(direction, update, timestamp);
84 }
85 propagateUpdate(direction, update, timestamp);
86 }
87
88 @Override
89 public ProjectionIndexer constructIndex(final TupleMask mask, final TraceInfo... traces) {
90 if (Options.employTrivialIndexers) {
91 if (nullMask.equals(mask)) {
92 final ProjectionIndexer indexer = getNullIndexer();
93 for (final TraceInfo traceInfo : traces) {
94 indexer.assignTraceInfo(traceInfo);
95 }
96 return indexer;
97 }
98 if (identityMask.equals(mask)) {
99 final ProjectionIndexer indexer = getIdentityIndexer();
100 for (final TraceInfo traceInfo : traces) {
101 indexer.assignTraceInfo(traceInfo);
102 }
103 return indexer;
104 }
105 }
106 return super.constructIndex(mask, traces);
107 }
108
109 public abstract ProjectionIndexer getNullIndexer();
110
111 public abstract ProjectionIndexer getIdentityIndexer();
112
113 @Override
114 public void appendParent(final Supplier supplier) {
115 parents.add(supplier);
116 }
117
118 @Override
119 public void removeParent(final Supplier supplier) {
120 parents.remove(supplier);
121 }
122
123 @Override
124 public Collection<Supplier> getParents() {
125 return parents;
126 }
127
128 @Override
129 public void assignTraceInfo(final TraceInfo traceInfo) {
130 super.assignTraceInfo(traceInfo);
131 if (traceInfo.propagateFromStandardNodeToSupplierParent()) {
132 for (final Supplier parent : parents) {
133 parent.acceptPropagatedTraceInfo(traceInfo);
134 }
135 }
136 }
137
138} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.single;
10
11import tools.refinery.viatra.runtime.matchers.backend.IUpdateable;
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.util.Direction;
14import tools.refinery.viatra.runtime.rete.misc.SimpleReceiver;
15import tools.refinery.viatra.runtime.rete.network.ReteContainer;
16import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
17
18/**
19 * @author Bergmann Gabor
20 *
21 */
22public class CallbackNode extends SimpleReceiver {
23
24 IUpdateable updateable;
25
26 public CallbackNode(ReteContainer reteContainer, IUpdateable updateable)
27 {
28 super(reteContainer);
29 this.updateable = updateable;
30 }
31
32 @Override
33 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
34 updateable.update(updateElement, direction == Direction.INSERT);
35 }
36
37}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.single;
11
12import java.util.Iterator;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
18import tools.refinery.viatra.runtime.rete.network.ProductionNode;
19import tools.refinery.viatra.runtime.rete.network.ReteContainer;
20import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery;
21import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
22
23/**
24 * Default implementation of the Production node, based on UniquenessEnforcerNode
25 *
26 * @author Gabor Bergmann
27 * @noinstantiate This class is not intended to be instantiated by clients.
28 */
29public class DefaultProductionNode extends UniquenessEnforcerNode implements ProductionNode {
30
31 protected final Map<String, Integer> posMapping;
32
33 /**
34 * @since 1.6
35 */
36 public DefaultProductionNode(final ReteContainer reteContainer, final Map<String, Integer> posMapping,
37 final boolean deleteRederiveEvaluation) {
38 this(reteContainer, posMapping, deleteRederiveEvaluation, null, null, null);
39 }
40
41 /**
42 * @since 1.6
43 */
44 public DefaultProductionNode(final ReteContainer reteContainer, final Map<String, Integer> posMapping,
45 final boolean deleteRederiveEvaluation, final TupleMask coreMask, final TupleMask posetMask,
46 final IPosetComparator posetComparator) {
47 super(reteContainer, posMapping.size(), deleteRederiveEvaluation, coreMask, posetMask, posetComparator);
48 this.posMapping = posMapping;
49 }
50
51 @Override
52 public Map<String, Integer> getPosMapping() {
53 return posMapping;
54 }
55
56 @Override
57 public Iterator<Tuple> iterator() {
58 return memory.iterator();
59 }
60
61 @Override
62 public void acceptPropagatedTraceInfo(final TraceInfo traceInfo) {
63 if (traceInfo.propagateToProductionNodeParentAlso()) {
64 super.acceptPropagatedTraceInfo(traceInfo);
65 }
66 }
67
68 @Override
69 public String toString() {
70 for (final TraceInfo traceInfo : this.traceInfos) {
71 if (traceInfo instanceof CompiledQuery) {
72 final String patternName = ((CompiledQuery) traceInfo).getPatternName();
73 return String.format(this.getClass().getName() + "<%s>=%s", patternName, super.toString());
74 }
75 }
76 return super.toString();
77 }
78
79}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.single;
10
11import java.util.Collection;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.Supplier;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22/**
23 * A bucket holds a filtered set of tuples of its parent {@link DiscriminatorDispatcherNode}.
24 * Exactly those that have the given bucket key at their discrimination column.
25 *
26 * <p> During operation, tuple contents and bucket keys have already been wrapped using {@link IQueryRuntimeContext#wrapElement(Object)}
27 *
28 * @author Gabor Bergmann
29 * @since 1.5
30 */
31public class DiscriminatorBucketNode extends SingleInputNode {
32
33 private Object bucketKey;
34
35 /**
36 * @param bucketKey will be wrapped using {@link IQueryRuntimeContext#wrapElement(Object)}
37
38 */
39 public DiscriminatorBucketNode(ReteContainer reteContainer, Object bucketKey) {
40 super(reteContainer);
41 this.bucketKey = reteContainer.getNetwork().getEngine().getRuntimeContext().wrapElement(bucketKey);
42 }
43
44 @Override
45 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
46 if (parent != null) {
47 getDispatcher().pullIntoFiltered(collector, bucketKey, flush);
48 }
49 }
50
51 @Override
52 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
53 if (parent != null) {
54 getDispatcher().pullIntoWithTimestampFiltered(collector, bucketKey, flush);
55 }
56 }
57
58 @Override
59 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
60 propagateUpdate(direction, updateElement, timestamp);
61 }
62
63 public Object getBucketKey() {
64 return bucketKey;
65 }
66
67 @Override
68 public void appendParent(Supplier supplier) {
69 if (! (supplier instanceof DiscriminatorDispatcherNode))
70 throw new IllegalArgumentException();
71 super.appendParent(supplier);
72 }
73
74 public DiscriminatorDispatcherNode getDispatcher() {
75 return (DiscriminatorDispatcherNode) parent;
76 }
77
78 @Override
79 protected String toStringCore() {
80 return String.format("%s<%s=='%s'>",
81 super.toStringCore(),
82 (getDispatcher() == null) ? "?" : getDispatcher().getDiscriminationColumnIndex(),
83 bucketKey);
84 }
85}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.single;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.HashMap;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.matchers.util.Direction;
21import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
22import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
23import tools.refinery.viatra.runtime.rete.network.Receiver;
24import tools.refinery.viatra.runtime.rete.network.ReteContainer;
25import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
26import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
27
28/**
29 * Node that sends tuples off to different buckets (attached as children of type {@link DiscriminatorBucketNode}), based
30 * on the value of a given column.
31 *
32 * <p>
33 * Tuple contents and bucket keys have already been wrapped using {@link IQueryRuntimeContext#wrapElement(Object)}
34 *
35 * @author Gabor Bergmann
36 * @since 1.5
37 */
38public class DiscriminatorDispatcherNode extends SingleInputNode implements NetworkStructureChangeSensitiveNode {
39
40 private int discriminationColumnIndex;
41 private Map<Object, DiscriminatorBucketNode> buckets = new HashMap<>();
42 private Map<Object, Mailbox> bucketMailboxes = new HashMap<>();
43
44 /**
45 * @param reteContainer
46 */
47 public DiscriminatorDispatcherNode(ReteContainer reteContainer, int discriminationColumnIndex) {
48 super(reteContainer);
49 this.discriminationColumnIndex = discriminationColumnIndex;
50 }
51
52 @Override
53 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
54 Object dispatchKey = updateElement.get(discriminationColumnIndex);
55 Mailbox bucketMailBox = bucketMailboxes.get(dispatchKey);
56 if (bucketMailBox != null) {
57 bucketMailBox.postMessage(direction, updateElement, timestamp);
58 }
59 }
60
61 public int getDiscriminationColumnIndex() {
62 return discriminationColumnIndex;
63 }
64
65 @Override
66 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
67 propagatePullInto(collector, flush);
68 }
69
70 @Override
71 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
72 propagatePullIntoWithTimestamp(collector, flush);
73 }
74
75 /**
76 * @since 2.3
77 */
78 public void pullIntoFiltered(final Collection<Tuple> collector, final Object bucketKey, final boolean flush) {
79 final ArrayList<Tuple> unfiltered = new ArrayList<Tuple>();
80 propagatePullInto(unfiltered, flush);
81 for (Tuple tuple : unfiltered) {
82 if (bucketKey.equals(tuple.get(discriminationColumnIndex))) {
83 collector.add(tuple);
84 }
85 }
86 }
87
88 /**
89 * @since 2.3
90 */
91 public void pullIntoWithTimestampFiltered(final Map<Tuple, Timeline<Timestamp>> collector, final Object bucketKey,
92 final boolean flush) {
93 final Map<Tuple, Timeline<Timestamp>> unfiltered = CollectionsFactory.createMap();
94 propagatePullIntoWithTimestamp(unfiltered, flush);
95 for (final Entry<Tuple, Timeline<Timestamp>> entry : unfiltered.entrySet()) {
96 if (bucketKey.equals(entry.getKey().get(discriminationColumnIndex))) {
97 collector.put(entry.getKey(), entry.getValue());
98 }
99 }
100 }
101
102 @Override
103 public void appendChild(Receiver receiver) {
104 super.appendChild(receiver);
105 if (receiver instanceof DiscriminatorBucketNode) {
106 DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver;
107 Object bucketKey = bucket.getBucketKey();
108 DiscriminatorBucketNode old = buckets.put(bucketKey, bucket);
109 if (old != null) {
110 throw new IllegalStateException();
111 }
112 bucketMailboxes.put(bucketKey, this.getCommunicationTracker().proxifyMailbox(this, bucket.getMailbox()));
113 }
114 }
115
116 /**
117 * @since 2.2
118 */
119 public Map<Object, Mailbox> getBucketMailboxes() {
120 return this.bucketMailboxes;
121 }
122
123 @Override
124 public void networkStructureChanged() {
125 bucketMailboxes.clear();
126 for (Receiver receiver : children) {
127 if (receiver instanceof DiscriminatorBucketNode) {
128 DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver;
129 Object bucketKey = bucket.getBucketKey();
130 bucketMailboxes.put(bucketKey,
131 this.getCommunicationTracker().proxifyMailbox(this, bucket.getMailbox()));
132 }
133 }
134 }
135
136 @Override
137 public void removeChild(Receiver receiver) {
138 super.removeChild(receiver);
139 if (receiver instanceof DiscriminatorBucketNode) {
140 DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver;
141 Object bucketKey = bucket.getBucketKey();
142 DiscriminatorBucketNode old = buckets.remove(bucketKey);
143 if (old != bucket)
144 throw new IllegalStateException();
145 bucketMailboxes.remove(bucketKey);
146 }
147 }
148
149 @Override
150 protected String toStringCore() {
151 return super.toStringCore() + '<' + discriminationColumnIndex + '>';
152 }
153
154}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.single;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.rete.network.ReteContainer;
14
15public class EqualityFilterNode extends FilterNode {
16
17 int[] indices;
18 int first;
19
20 /**
21 * @param reteContainer
22 * @param indices
23 * indices of the Tuple that should hold equal values
24 */
25 public EqualityFilterNode(ReteContainer reteContainer, int[] indices) {
26 super(reteContainer);
27 this.indices = indices;
28 first = indices[0];
29 }
30
31 @Override
32 public boolean check(Tuple ps) {
33 Object firstElement = ps.get(first);
34 for (int i = 1 /* first is omitted */; i < indices.length; i++) {
35 if (!ps.get(indices[i]).equals(firstElement))
36 return false;
37 }
38 return true;
39 }
40
41}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.single;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Map.Entry;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.network.ReteContainer;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22/**
23 * This node implements a simple filter. A stateless abstract check() predicate determines whether a matching is allowed
24 * to pass.
25 *
26 * @author Gabor Bergmann
27 *
28 */
29public abstract class FilterNode extends SingleInputNode {
30
31 public FilterNode(final ReteContainer reteContainer) {
32 super(reteContainer);
33 }
34
35 /**
36 * Abstract filtering predicate. Expected to be stateless.
37 *
38 * @param ps
39 * the matching to be checked.
40 * @return true if and only if the parameter matching is allowed to pass through this node.
41 */
42 public abstract boolean check(final Tuple ps);
43
44 @Override
45 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
46 for (final Tuple ps : this.reteContainer.pullPropagatedContents(this, flush)) {
47 if (check(ps)) {
48 collector.add(ps);
49 }
50 }
51 }
52
53 @Override
54 public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) {
55 for (final Entry<Tuple, Timeline<Timestamp>> entry : this.reteContainer.pullPropagatedContentsWithTimestamp(this, flush).entrySet()) {
56 if (check(entry.getKey())) {
57 collector.put(entry.getKey(), entry.getValue());
58 }
59 }
60 }
61
62 @Override
63 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
64 if (check(updateElement)) {
65 propagateUpdate(direction, updateElement, timestamp);
66 }
67 }
68
69}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.single;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
14import tools.refinery.viatra.runtime.rete.network.ReteContainer;
15
16/**
17 * This node filters patterns according to equalities and inequalities of elements. The 'subject' element is asserted to
18 * be different from the elements given by the inequalityMask.
19 *
20 *
21 * @author Gabor Bergmann
22 *
23 */
24public class InequalityFilterNode extends FilterNode {
25
26 int subjectIndex;
27 TupleMask inequalityMask;
28
29 /**
30 * @param reteContainer
31 * @param subject
32 * the index of the element that should be compared.
33 * @param inequalityMask
34 * the indices of elements that should be different from the subjectIndex.
35 */
36 public InequalityFilterNode(ReteContainer reteContainer, int subject, TupleMask inequalityMask) {
37 super(reteContainer);
38 this.subjectIndex = subject;
39 this.inequalityMask = inequalityMask;
40 }
41
42 @Override
43 public boolean check(Tuple ps) {
44 Object subject = ps.get(subjectIndex);
45 for (int ineq : inequalityMask.indices) {
46 if (subject.equals(ps.get(ineq)))
47 return false;
48 }
49 return true;
50 }
51
52}
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..95018c4f
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java
@@ -0,0 +1,125 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, Istvan Rath and Daniel Varro
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-v20.html.
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.single;
10
11import tools.refinery.viatra.runtime.rete.itc.alg.representative.RepresentativeElectionAlgorithm;
12import tools.refinery.viatra.runtime.rete.itc.alg.representative.RepresentativeObserver;
13import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph;
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
16import tools.refinery.viatra.runtime.matchers.util.Clearable;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.network.ReinitializedNode;
20import tools.refinery.viatra.runtime.rete.network.ReteContainer;
21import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
22
23import java.util.Collection;
24import java.util.Map;
25
26public class RepresentativeElectionNode extends SingleInputNode implements Clearable, RepresentativeObserver,
27 ReinitializedNode {
28 private final RepresentativeElectionAlgorithm.Factory algorithmFactory;
29 private Graph<Object> graph;
30 private RepresentativeElectionAlgorithm algorithm;
31
32 public RepresentativeElectionNode(ReteContainer reteContainer,
33 RepresentativeElectionAlgorithm.Factory algorithmFactory) {
34 super(reteContainer);
35 this.algorithmFactory = algorithmFactory;
36 graph = new Graph<>();
37 algorithm = algorithmFactory.create(graph);
38 algorithm.setObserver(this);
39 reteContainer.registerClearable(this);
40 }
41
42 @Override
43 public void networkStructureChanged() {
44 if (reteContainer.isTimelyEvaluation() && reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
45 throw new IllegalStateException(this + " cannot be used in recursive differential dataflow evaluation!");
46 }
47 super.networkStructureChanged();
48 }
49
50 @Override
51 public void reinitializeWith(Collection<Tuple> tuples) {
52 algorithm.dispose();
53 graph = new Graph<>();
54 for (var tuple : tuples) {
55 insertEdge(tuple.get(0), tuple.get(1));
56 }
57 algorithm = algorithmFactory.create(graph);
58 algorithm.setObserver(this);
59 }
60
61 @Override
62 public void tupleChanged(Object source, Object representative, Direction direction) {
63 var tuple = Tuples.staticArityFlatTupleOf(source, representative);
64 propagateUpdate(direction, tuple, Timestamp.ZERO);
65 }
66
67 @Override
68 public void clear() {
69 algorithm.dispose();
70 graph = new Graph<>();
71 algorithm = algorithmFactory.create(graph);
72 }
73
74 @Override
75 public void update(Direction direction, Tuple updateElement, Timestamp timestamp) {
76 var source = updateElement.get(0);
77 var target = updateElement.get(1);
78 switch (direction) {
79 case INSERT -> insertEdge(source, target);
80 case DELETE -> deleteEdge(source, target);
81 default -> throw new IllegalArgumentException("Unknown direction: " + direction);
82 }
83 }
84
85 private void insertEdge(Object source, Object target) {
86 graph.insertNode(source);
87 graph.insertNode(target);
88 graph.insertEdge(source, target);
89 }
90
91 private void deleteEdge(Object source, Object target) {
92 graph.deleteEdgeIfExists(source, target);
93 if (isIsolated(source)) {
94 graph.deleteNode(source);
95 }
96 if (!source.equals(target) && isIsolated(target)) {
97 graph.deleteNode(target);
98 }
99 }
100
101 private boolean isIsolated(Object node) {
102 return graph.getTargetNodes(node).isEmpty() && graph.getSourceNodes(node).isEmpty();
103 }
104
105 @Override
106 public void pullInto(Collection<Tuple> collector, boolean flush) {
107 for (var entry : algorithm.getComponents().entrySet()) {
108 var representative = entry.getKey();
109 for (var node : entry.getValue()) {
110 collector.add(Tuples.staticArityFlatTupleOf(node, representative));
111 }
112 }
113 }
114
115 @Override
116 public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) {
117 // Use all zero timestamps because this node cannot be used in recursive groups anyway.
118 for (var entry : algorithm.getComponents().entrySet()) {
119 var representative = entry.getKey();
120 for (var node : entry.getValue()) {
121 collector.put(Tuples.staticArityFlatTupleOf(node, representative), Timestamp.INSERT_AT_ZERO_TIMELINE);
122 }
123 }
124 }
125}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.single;
11
12import java.util.Collection;
13import java.util.Collections;
14import java.util.Map;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.StandardNode;
20import tools.refinery.viatra.runtime.rete.network.Supplier;
21import tools.refinery.viatra.runtime.rete.network.Tunnel;
22import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker;
23import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
24import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
25import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
26import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
27import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
28
29/**
30 * @author Gabor Bergmann
31 *
32 */
33public abstract class SingleInputNode extends StandardNode implements Tunnel {
34
35 protected Supplier parent;
36 /**
37 * @since 1.6
38 */
39 protected Mailbox mailbox;
40
41 public SingleInputNode(ReteContainer reteContainer) {
42 super(reteContainer);
43 mailbox = instantiateMailbox();
44 reteContainer.registerClearable(mailbox);
45 parent = null;
46 }
47
48 /**
49 * Instantiates the {@link Mailbox} of this receiver.
50 * Subclasses may override this method to provide their own mailbox implementation.
51 *
52 * @return the mailbox
53 * @since 2.0
54 */
55 protected Mailbox instantiateMailbox() {
56 if (this.reteContainer.isTimelyEvaluation()) {
57 return new TimelyMailbox(this, this.reteContainer);
58 } else {
59 return new BehaviorChangingMailbox(this, this.reteContainer);
60 }
61 }
62
63 @Override
64 public CommunicationTracker getCommunicationTracker() {
65 return this.reteContainer.getCommunicationTracker();
66 }
67
68 @Override
69 public Mailbox getMailbox() {
70 return this.mailbox;
71 }
72
73 @Override
74 public void appendParent(Supplier supplier) {
75 if (parent == null)
76 parent = supplier;
77 else
78 throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent
79 + ") and cannot connect to additional parent (" + supplier
80 + ") as it is not a Uniqueness Enforcer Node. ");
81 }
82
83 @Override
84 public void removeParent(Supplier supplier) {
85 if (parent == supplier)
86 parent = null;
87 else
88 throw new IllegalArgumentException("Illegal RETE edge removal: the parent of " + this + " is not "
89 + supplier);
90 }
91
92 /**
93 * To be called by derived classes and ReteContainer.
94 */
95 public void propagatePullInto(final Collection<Tuple> collector, final boolean flush) {
96 if (parent != null) {
97 parent.pullInto(collector, flush);
98 }
99 }
100
101 /**
102 * To be called by derived classes and ReteContainer.
103 */
104 public void propagatePullIntoWithTimestamp(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
105 if (parent != null) {
106 parent.pullIntoWithTimeline(collector, flush);
107 }
108 }
109
110 @Override
111 public Collection<Supplier> getParents() {
112 if (parent == null)
113 return Collections.emptySet();
114 else
115 return Collections.singleton(parent);
116 }
117
118 @Override
119 public void assignTraceInfo(TraceInfo traceInfo) {
120 super.assignTraceInfo(traceInfo);
121 if (traceInfo.propagateFromStandardNodeToSupplierParent())
122 if (parent != null)
123 parent.acceptPropagatedTraceInfo(traceInfo);
124 }
125
126}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.single;
10
11import java.util.Iterator;
12import java.util.Map;
13
14import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
15import tools.refinery.viatra.runtime.rete.network.ProductionNode;
16import tools.refinery.viatra.runtime.rete.network.ReteContainer;
17import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery;
18import tools.refinery.viatra.runtime.rete.traceability.TraceInfo;
19/**
20 * Differential dataflow implementation of the Production node, based on {@link TimelyUniquenessEnforcerNode}.
21 *
22 * @author Tamas Szabo
23 * @noinstantiate This class is not intended to be instantiated by clients.
24 * @since 2.3
25 */
26public class TimelyProductionNode extends TimelyUniquenessEnforcerNode implements ProductionNode {
27
28 protected final Map<String, Integer> posMapping;
29
30 public TimelyProductionNode(final ReteContainer reteContainer, final Map<String, Integer> posMapping) {
31 super(reteContainer, posMapping.size());
32 this.posMapping = posMapping;
33 }
34
35 @Override
36 public Map<String, Integer> getPosMapping() {
37 return this.posMapping;
38 }
39
40 @Override
41 public Iterator<Tuple> iterator() {
42 return this.memory.keySet().iterator();
43 }
44
45 @Override
46 public void acceptPropagatedTraceInfo(final TraceInfo traceInfo) {
47 if (traceInfo.propagateToProductionNodeParentAlso()) {
48 super.acceptPropagatedTraceInfo(traceInfo);
49 }
50 }
51
52 @Override
53 public String toString() {
54 for (final TraceInfo traceInfo : this.traceInfos) {
55 if (traceInfo instanceof CompiledQuery) {
56 final String patternName = ((CompiledQuery) traceInfo).getPatternName();
57 return String.format(this.getClass().getName() + "<%s>=%s", patternName, super.toString());
58 }
59 }
60 return super.toString();
61 }
62
63}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.single;
10
11import java.util.Collection;
12import java.util.Map;
13import java.util.Map.Entry;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.matchers.util.Signed;
19import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
21import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
22import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
23import tools.refinery.viatra.runtime.rete.index.timely.TimelyMemoryIdentityIndexer;
24import tools.refinery.viatra.runtime.rete.index.timely.TimelyMemoryNullIndexer;
25import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
26import tools.refinery.viatra.runtime.rete.network.ReteContainer;
27import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
28import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
29import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode;
30import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
31import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox;
32
33/**
34 * Timely uniqueness enforcer node implementation.
35 *
36 * @author Tamas Szabo
37 * @noinstantiate This class is not intended to be instantiated by clients.
38 * @noextend This class is not intended to be subclassed by clients.
39 * @since 2.4
40 */
41public class TimelyUniquenessEnforcerNode extends AbstractUniquenessEnforcerNode implements ResumableNode {
42
43 protected final TimelyMemory<Timestamp> memory;
44 /**
45 * @since 2.4
46 */
47 protected CommunicationGroup group;
48
49 public TimelyUniquenessEnforcerNode(final ReteContainer container, final int tupleWidth) {
50 super(container, tupleWidth);
51 this.memory = new TimelyMemory<Timestamp>(
52 container.getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL);
53 container.registerClearable(this.memory);
54 this.mailbox = instantiateMailbox();
55 container.registerClearable(this.mailbox);
56 }
57
58 protected Mailbox instantiateMailbox() {
59 return new TimelyMailbox(this, this.reteContainer);
60 }
61
62 @Override
63 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
64 for (final Tuple tuple : this.memory.getTuplesAtInfinity()) {
65 collector.add(tuple);
66 }
67 }
68
69 @Override
70 public CommunicationGroup getCurrentGroup() {
71 return this.group;
72 }
73
74 @Override
75 public void setCurrentGroup(final CommunicationGroup group) {
76 this.group = group;
77 }
78
79 @Override
80 public Set<Tuple> getTuples() {
81 return this.memory.getTuplesAtInfinity();
82 }
83
84 /**
85 * @since 2.4
86 */
87 @Override
88 public Timestamp getResumableTimestamp() {
89 return this.memory.getResumableTimestamp();
90 }
91
92 /**
93 * @since 2.4
94 */
95 @Override
96 public void resumeAt(final Timestamp timestamp) {
97 final Map<Tuple, Diff<Timestamp>> diffMap = this.memory.resumeAt(timestamp);
98 for (final Entry<Tuple, Diff<Timestamp>> entry : diffMap.entrySet()) {
99 for (final Signed<Timestamp> signed : entry.getValue()) {
100 propagate(signed.getDirection(), entry.getKey(), signed.getPayload());
101 }
102 }
103 final Timestamp nextTimestamp = this.memory.getResumableTimestamp();
104 if (nextTimestamp != null) {
105 this.group.notifyHasMessage(this.mailbox, nextTimestamp);
106 }
107 }
108
109 @Override
110 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
111 Diff<Timestamp> resultDiff = null;
112 if (direction == Direction.INSERT) {
113 resultDiff = this.memory.put(update, timestamp);
114 } else {
115 try {
116 resultDiff = this.memory.remove(update, timestamp);
117 } catch (final IllegalStateException e) {
118 issueError("[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in "
119 + this.getClass().getName() + " " + this + " for pattern(s) "
120 + getTraceInfoPatternsEnumerated(), e);
121 // diff will remain unset in case of the exception, it is time to return
122 return;
123 }
124 }
125
126 for (final Signed<Timestamp> signed : resultDiff) {
127 propagate(signed.getDirection(), update, signed.getPayload());
128 }
129 }
130
131 @Override
132 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
133 collector.putAll(this.memory.asMap());
134 }
135
136 @Override
137 public ProjectionIndexer getNullIndexer() {
138 if (this.memoryNullIndexer == null) {
139 this.memoryNullIndexer = new TimelyMemoryNullIndexer(this.reteContainer, this.tupleWidth, this.memory, this,
140 this, this.specializedListeners);
141 this.getCommunicationTracker().registerDependency(this, this.memoryNullIndexer);
142 }
143 return this.memoryNullIndexer;
144 }
145
146 @Override
147 public ProjectionIndexer getIdentityIndexer() {
148 if (this.memoryIdentityIndexer == null) {
149 this.memoryIdentityIndexer = new TimelyMemoryIdentityIndexer(this.reteContainer, this.tupleWidth,
150 this.memory, this, this, this.specializedListeners);
151 this.getCommunicationTracker().registerDependency(this, this.memoryIdentityIndexer);
152 }
153 return this.memoryIdentityIndexer;
154 }
155
156 @Override
157 public void networkStructureChanged() {
158 super.networkStructureChanged();
159 }
160
161} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.single;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Map.Entry;
15
16import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
17import tools.refinery.viatra.runtime.matchers.util.Direction;
18import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
19import tools.refinery.viatra.runtime.rete.network.ReteContainer;
20import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
21
22public abstract class TransformerNode extends SingleInputNode {
23
24 public TransformerNode(final ReteContainer reteContainer) {
25 super(reteContainer);
26 }
27
28 protected abstract Tuple transform(final Tuple input);
29
30 @Override
31 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
32 for (Tuple ps : reteContainer.pullPropagatedContents(this, flush)) {
33 collector.add(transform(ps));
34 }
35 }
36
37 @Override
38 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
39 for (final Entry<Tuple, Timeline<Timestamp>> entry : reteContainer.pullPropagatedContentsWithTimestamp(this, flush).entrySet()) {
40 collector.put(transform(entry.getKey()), entry.getValue());
41 }
42 }
43
44 @Override
45 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
46 propagateUpdate(direction, transform(updateElement), timestamp);
47 }
48
49}
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..fdda4ef4
--- /dev/null
+++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java
@@ -0,0 +1,147 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.single;
10
11import tools.refinery.viatra.runtime.rete.itc.alg.incscc.IncSCCAlg;
12import tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple;
13import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph;
14import tools.refinery.viatra.runtime.rete.itc.igraph.ITcDataSource;
15import tools.refinery.viatra.runtime.rete.itc.igraph.ITcObserver;
16import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
17import tools.refinery.viatra.runtime.matchers.util.Clearable;
18import tools.refinery.viatra.runtime.matchers.util.Direction;
19import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
20import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode;
21import tools.refinery.viatra.runtime.rete.network.ReinitializedNode;
22import tools.refinery.viatra.runtime.rete.network.ReteContainer;
23import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
24import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
25
26import java.util.Collection;
27import java.util.Map;
28
29/**
30 * This class represents a transitive closure node in the Rete net.
31 * <p>
32 * This node must not be used in recursive {@link CommunicationGroup}s.
33 *
34 * @author Gabor Bergmann
35 *
36 */
37public class TransitiveClosureNode extends SingleInputNode
38 implements Clearable, ITcObserver<Object>, NetworkStructureChangeSensitiveNode, ReinitializedNode {
39
40 private Graph<Object> graphDataSource;
41 private ITcDataSource<Object> transitiveClosureAlgorithm;
42
43 /**
44 * Create a new transitive closure rete node.
45 *
46 * Client may optionally call {@link #reinitializeWith(Collection)} before using the node, instead of inserting the
47 * initial set of tuples one by one.
48 *
49 * @param reteContainer
50 * the rete container of the node
51 */
52 public TransitiveClosureNode(ReteContainer reteContainer) {
53 super(reteContainer);
54 graphDataSource = new Graph<Object>();
55 transitiveClosureAlgorithm = new IncSCCAlg<Object>(graphDataSource);
56 transitiveClosureAlgorithm.attachObserver(this);
57 reteContainer.registerClearable(this);
58 }
59
60 @Override
61 public void networkStructureChanged() {
62 if (this.reteContainer.isTimelyEvaluation() && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) {
63 throw new IllegalStateException(this.toString() + " cannot be used in recursive differential dataflow evaluation!");
64 }
65 super.networkStructureChanged();
66 }
67
68 /**
69 * Initializes the graph data source with the given collection of tuples.
70 *
71 * @param tuples
72 * the initial collection of tuples
73 */
74 @Override
75 public void reinitializeWith(Collection<tools.refinery.viatra.runtime.matchers.tuple.Tuple> tuples) {
76 clear();
77
78 for (tools.refinery.viatra.runtime.matchers.tuple.Tuple t : tuples) {
79 graphDataSource.insertNode(t.get(0));
80 graphDataSource.insertNode(t.get(1));
81 graphDataSource.insertEdge(t.get(0), t.get(1));
82 }
83 transitiveClosureAlgorithm.attachObserver(this);
84 }
85
86 @Override
87 public void pullInto(final Collection<tools.refinery.viatra.runtime.matchers.tuple.Tuple> collector, final boolean flush) {
88 for (final Tuple<Object> tuple : ((IncSCCAlg<Object>) transitiveClosureAlgorithm).getTcRelation()) {
89 collector.add(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget()));
90 }
91 }
92
93 @Override
94 public void pullIntoWithTimeline(
95 final Map<tools.refinery.viatra.runtime.matchers.tuple.Tuple, Timeline<Timestamp>> collector,
96 final boolean flush) {
97 // use all zero timestamps because this node cannot be used in recursive groups anyway
98 for (final Tuple<Object> tuple : ((IncSCCAlg<Object>) transitiveClosureAlgorithm).getTcRelation()) {
99 collector.put(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget()), Timestamp.INSERT_AT_ZERO_TIMELINE);
100 }
101 }
102
103 @Override
104 public void update(Direction direction, tools.refinery.viatra.runtime.matchers.tuple.Tuple updateElement,
105 Timestamp timestamp) {
106 if (updateElement.getSize() == 2) {
107 Object source = updateElement.get(0);
108 Object target = updateElement.get(1);
109
110 if (direction == Direction.INSERT) {
111 graphDataSource.insertNode(source);
112 graphDataSource.insertNode(target);
113 graphDataSource.insertEdge(source, target);
114 }
115 if (direction == Direction.DELETE) {
116 graphDataSource.deleteEdgeIfExists(source, target);
117
118 if (((IncSCCAlg<Object>) transitiveClosureAlgorithm).isIsolated(source)) {
119 graphDataSource.deleteNode(source);
120 }
121 if (!source.equals(target) && ((IncSCCAlg<Object>) transitiveClosureAlgorithm).isIsolated(target)) {
122 graphDataSource.deleteNode(target);
123 }
124 }
125 }
126 }
127
128 @Override
129 public void clear() {
130 transitiveClosureAlgorithm.dispose();
131 graphDataSource = new Graph<Object>();
132 transitiveClosureAlgorithm = new IncSCCAlg<Object>(graphDataSource);
133 }
134
135 @Override
136 public void tupleInserted(Object source, Object target) {
137 tools.refinery.viatra.runtime.matchers.tuple.Tuple tuple = Tuples.staticArityFlatTupleOf(source, target);
138 propagateUpdate(Direction.INSERT, tuple, Timestamp.ZERO);
139 }
140
141 @Override
142 public void tupleDeleted(Object source, Object target) {
143 tools.refinery.viatra.runtime.matchers.tuple.Tuple tuple = Tuples.staticArityFlatTupleOf(source, target);
144 propagateUpdate(Direction.DELETE, tuple, Timestamp.ZERO);
145 }
146
147}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.single;
11
12import java.util.Collection;
13import java.util.Map;
14
15import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
16import tools.refinery.viatra.runtime.matchers.util.Direction;
17import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
18import tools.refinery.viatra.runtime.rete.network.ReteContainer;
19import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
20
21/**
22 * Simply propagates everything. Might be used to join or fork.
23 *
24 * @author Gabor Bergmann
25 */
26public class TransparentNode extends SingleInputNode {
27
28 public TransparentNode(final ReteContainer reteContainer) {
29 super(reteContainer);
30 }
31
32 @Override
33 public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) {
34 propagateUpdate(direction, updateElement, timestamp);
35
36 }
37
38 @Override
39 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
40 propagatePullInto(collector, flush);
41 }
42
43 @Override
44 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
45 propagatePullIntoWithTimestamp(collector, flush);
46 }
47
48}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.single;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
14import tools.refinery.viatra.runtime.rete.network.ReteContainer;
15
16/**
17 * Trims the matchings as specified by a mask.
18 *
19 * @author Gabor Bergmann
20 *
21 */
22public class TrimmerNode extends TransformerNode {
23
24 protected TupleMask mask;
25
26 /**
27 * @param reteContainer
28 * @param mask
29 * The mask used to trim substitutions.
30 */
31 public TrimmerNode(ReteContainer reteContainer, TupleMask mask) {
32 super(reteContainer);
33 this.mask = mask;
34 }
35
36 public TrimmerNode(ReteContainer reteContainer) {
37 super(reteContainer);
38 this.mask = null;
39 }
40
41 /**
42 * @return the mask
43 */
44 public TupleMask getMask() {
45 return mask;
46 }
47
48 /**
49 * @param mask
50 * the mask to set
51 */
52 public void setMask(TupleMask mask) {
53 this.mask = mask;
54 }
55
56 @Override
57 protected Tuple transform(Tuple input) {
58 return mask.transform(input);
59 }
60
61}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann, Tamas Szabo and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.single;
11
12import java.util.Collection;
13import java.util.Map;
14import java.util.Set;
15
16import tools.refinery.viatra.runtime.matchers.context.IPosetComparator;
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
19import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
20import tools.refinery.viatra.runtime.matchers.util.Direction;
21import tools.refinery.viatra.runtime.matchers.util.IMultiset;
22import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
23import tools.refinery.viatra.runtime.rete.index.MemoryIdentityIndexer;
24import tools.refinery.viatra.runtime.rete.index.MemoryNullIndexer;
25import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer;
26import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver;
27import tools.refinery.viatra.runtime.rete.network.RederivableNode;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
30import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
31import tools.refinery.viatra.runtime.rete.network.communication.timeless.RecursiveCommunicationGroup;
32import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox;
33import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox;
34import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.PosetAwareMailbox;
35
36/**
37 * Timeless uniqueness enforcer node implementation.
38 * <p>
39 * The node is capable of operating in the delete and re-derive mode. In this mode, it is also possible to equip the
40 * node with an {@link IPosetComparator} to identify monotone changes; thus, ensuring that a fix-point can be reached
41 * during the evaluation.
42 *
43 * @author Gabor Bergmann
44 * @author Tamas Szabo
45 * @noinstantiate This class is not intended to be instantiated by clients.
46 * @noextend This class is not intended to be subclassed by clients.
47 */
48public class UniquenessEnforcerNode extends AbstractUniquenessEnforcerNode
49 implements RederivableNode, PosetAwareReceiver {
50
51 protected IMultiset<Tuple> memory;
52 /**
53 * @since 1.6
54 */
55 protected IMultiset<Tuple> rederivableMemory;
56 /**
57 * @since 1.6
58 */
59 protected boolean deleteRederiveEvaluation;
60
61 /**
62 * @since 1.7
63 */
64 protected CommunicationGroup currentGroup;
65
66 public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth) {
67 this(reteContainer, tupleWidth, false);
68 }
69
70 /**
71 * OPTIONAL ELEMENT - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE
72 *
73 * @since 1.6
74 */
75 protected final TupleMask coreMask;
76 /**
77 * OPTIONAL ELEMENTS - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE
78 *
79 * @since 1.6
80 */
81 protected final TupleMask posetMask;
82 /**
83 * OPTIONAL ELEMENTS - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE
84 *
85 * @since 1.6
86 */
87 protected final IPosetComparator posetComparator;
88
89 /**
90 * @since 1.6
91 */
92 public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth,
93 final boolean deleteRederiveEvaluation) {
94 this(reteContainer, tupleWidth, deleteRederiveEvaluation, null, null, null);
95 }
96
97 /**
98 * @since 1.6
99 */
100 public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth,
101 final boolean deleteRederiveEvaluation, final TupleMask coreMask, final TupleMask posetMask,
102 final IPosetComparator posetComparator) {
103 super(reteContainer, tupleWidth);
104 this.memory = CollectionsFactory.createMultiset();
105 this.rederivableMemory = CollectionsFactory.createMultiset();
106 reteContainer.registerClearable(this.memory);
107 reteContainer.registerClearable(this.rederivableMemory);
108 this.deleteRederiveEvaluation = deleteRederiveEvaluation;
109 this.coreMask = coreMask;
110 this.posetMask = posetMask;
111 this.posetComparator = posetComparator;
112 this.mailbox = instantiateMailbox();
113 reteContainer.registerClearable(this.mailbox);
114 }
115
116 @Override
117 public void pullInto(final Collection<Tuple> collector, final boolean flush) {
118 for (final Tuple tuple : this.memory.distinctValues()) {
119 collector.add(tuple);
120 }
121 }
122
123 /**
124 * @since 2.8
125 */
126 @Override
127 public Set<Tuple> getTuples() {
128 return this.memory.distinctValues();
129 }
130
131 @Override
132 public boolean isInDRedMode() {
133 return this.deleteRederiveEvaluation;
134 }
135
136 @Override
137 public TupleMask getCoreMask() {
138 return coreMask;
139 }
140
141 @Override
142 public TupleMask getPosetMask() {
143 return posetMask;
144 }
145
146 @Override
147 public IPosetComparator getPosetComparator() {
148 return posetComparator;
149 }
150
151 @Override
152 public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) {
153 throw new UnsupportedOperationException("Use the timely version of this node!");
154 }
155
156 /**
157 * @since 2.0
158 */
159 protected Mailbox instantiateMailbox() {
160 if (coreMask != null && posetMask != null && posetComparator != null) {
161 return new PosetAwareMailbox(this, this.reteContainer);
162 } else {
163 return new BehaviorChangingMailbox(this, this.reteContainer);
164 }
165 }
166
167 @Override
168 public void update(final Direction direction, final Tuple update, final Timestamp timestamp) {
169 updateWithPosetInfo(direction, update, false);
170 }
171
172 @Override
173 public void updateWithPosetInfo(final Direction direction, final Tuple update, final boolean monotone) {
174 if (this.deleteRederiveEvaluation) {
175 if (updateWithDeleteAndRederive(direction, update, monotone)) {
176 propagate(direction, update, Timestamp.ZERO);
177 }
178 } else {
179 if (updateDefault(direction, update)) {
180 propagate(direction, update, Timestamp.ZERO);
181 }
182 }
183 }
184
185 /**
186 * @since 2.4
187 */
188 protected boolean updateWithDeleteAndRederive(final Direction direction, final Tuple update,
189 final boolean monotone) {
190 boolean propagate = false;
191
192 final int memoryCount = memory.getCount(update);
193 final int rederivableCount = rederivableMemory.getCount(update);
194
195 if (direction == Direction.INSERT) {
196 // INSERT
197 if (rederivableCount != 0) {
198 // the tuple is in the re-derivable memory
199 rederivableMemory.addOne(update);
200 if (rederivableMemory.isEmpty()) {
201 // there is nothing left to be re-derived
202 // this can happen if the INSERT cancelled out a DELETE
203 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
204 }
205 } else {
206 // the tuple is in the main memory
207 propagate = memory.addOne(update);
208 }
209 } else {
210 // DELETE
211 if (rederivableCount != 0) {
212 // the tuple is in the re-derivable memory
213 if (memoryCount != 0) {
214 issueError("[INTERNAL ERROR] Inconsistent state for " + update
215 + " because it is present both in the main and re-derivable memory in the UniquenessEnforcerNode "
216 + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(), null);
217 }
218
219 try {
220 rederivableMemory.removeOne(update);
221 } catch (final IllegalStateException ex) {
222 issueError(
223 "[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in UniquenessEnforcer "
224 + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(),
225 ex);
226 }
227 if (rederivableMemory.isEmpty()) {
228 // there is nothing left to be re-derived
229 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
230 }
231 } else {
232 // the tuple is in the main memory
233 if (monotone) {
234 propagate = memory.removeOne(update);
235 } else {
236 final int count = memoryCount - 1;
237 if (count > 0) {
238 if (rederivableMemory.isEmpty()) {
239 // there is now something to be re-derived
240 ((RecursiveCommunicationGroup) currentGroup).addRederivable(this);
241 }
242 rederivableMemory.addPositive(update, count);
243 }
244 memory.clearAllOf(update);
245 propagate = true;
246 }
247 }
248 }
249
250 return propagate;
251 }
252
253 /**
254 * @since 2.4
255 */
256 protected boolean updateDefault(final Direction direction, final Tuple update) {
257 boolean propagate = false;
258 if (direction == Direction.INSERT) {
259 // INSERT
260 propagate = memory.addOne(update);
261 } else {
262 // DELETE
263 try {
264 propagate = memory.removeOne(update);
265 } catch (final IllegalStateException ex) {
266 propagate = false;
267 issueError("[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in "
268 + this.getClass().getName() + " " + this + " for pattern(s) "
269 + getTraceInfoPatternsEnumerated(), ex);
270 }
271 }
272 return propagate;
273 }
274
275 /**
276 * @since 1.6
277 */
278 @Override
279 public void rederiveOne() {
280 final Tuple update = rederivableMemory.iterator().next();
281 final int count = rederivableMemory.getCount(update);
282 rederivableMemory.clearAllOf(update);
283 memory.addPositive(update, count);
284 // if there is no other re-derivable tuple, then unregister the node itself
285 if (this.rederivableMemory.isEmpty()) {
286 ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this);
287 }
288 propagate(Direction.INSERT, update, Timestamp.ZERO);
289 }
290
291 @Override
292 public ProjectionIndexer getNullIndexer() {
293 if (this.memoryNullIndexer == null) {
294 this.memoryNullIndexer = new MemoryNullIndexer(this.reteContainer, this.tupleWidth,
295 this.memory.distinctValues(), this, this, this.specializedListeners);
296 this.getCommunicationTracker().registerDependency(this, this.memoryNullIndexer);
297 }
298 return this.memoryNullIndexer;
299 }
300
301 @Override
302 public ProjectionIndexer getIdentityIndexer() {
303 if (this.memoryIdentityIndexer == null) {
304 this.memoryIdentityIndexer = new MemoryIdentityIndexer(this.reteContainer, this.tupleWidth,
305 this.memory.distinctValues(), this, this, this.specializedListeners);
306 this.getCommunicationTracker().registerDependency(this, this.memoryIdentityIndexer);
307 }
308 return this.memoryIdentityIndexer;
309 }
310
311 @Override
312 public CommunicationGroup getCurrentGroup() {
313 return currentGroup;
314 }
315
316 @Override
317 public void setCurrentGroup(final CommunicationGroup currentGroup) {
318 this.currentGroup = currentGroup;
319 }
320
321}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.single;
11
12import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
13import tools.refinery.viatra.runtime.rete.network.ReteContainer;
14
15/**
16 * A filter node that keeps only those tuples that contain a certain value at a certain position.
17 *
18 * @author Bergmann Gabor
19 *
20 */
21public class ValueBinderFilterNode extends FilterNode {
22
23 int bindingIndex;
24 Object bindingValue;
25
26 /**
27 * @param reteContainer
28 * @param bindingIndex
29 * the position in the tuple that should be bound
30 * @param bindingValue
31 * the value to which the tuple has to be bound
32 */
33 public ValueBinderFilterNode(ReteContainer reteContainer, int bindingIndex, Object bindingValue) {
34 super(reteContainer);
35 this.bindingIndex = bindingIndex;
36 this.bindingValue = bindingValue;
37 }
38
39 @Override
40 public boolean check(Tuple ps) {
41 return bindingValue.equals(ps.get(bindingIndex));
42 }
43
44}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.traceability;
10
11import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
12
13public class ActiveNodeConflictTrace extends RecipeTraceInfo { // TODO implement PatternTraceInfo
14 RecipeTraceInfo inactiveRecipeTrace;
15 public ActiveNodeConflictTrace(ReteNodeRecipe recipe,
16 RecipeTraceInfo parentRecipeTrace,
17 RecipeTraceInfo inactiveRecipeTrace) {
18 super(recipe, parentRecipeTrace);
19 this.inactiveRecipeTrace = inactiveRecipeTrace;
20 }
21 public RecipeTraceInfo getInactiveRecipeTrace() {
22 return inactiveRecipeTrace;
23 }
24} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.traceability;
10
11import java.util.Map;
12
13import tools.refinery.viatra.runtime.matchers.psystem.PBody;
14import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
15import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
16
17/**
18 * Indicates that recipe expresses the finished match set of a query.
19 * @author Bergmann Gabor
20 * @noinstantiate This class is not intended to be instantiated by clients.
21 */
22public class CompiledQuery extends RecipeTraceInfo implements
23 PatternTraceInfo {
24
25 private PQuery query;
26 private final Map<PBody, ? extends RecipeTraceInfo> parentRecipeTracesPerBody;
27
28 /**
29 * @since 1.6
30 */
31 public CompiledQuery(ReteNodeRecipe recipe,
32 Map<PBody, ? extends RecipeTraceInfo> parentRecipeTraces,
33 PQuery query) {
34 super(recipe, parentRecipeTraces.values());
35 parentRecipeTracesPerBody = parentRecipeTraces;
36 this.query = query;
37 }
38 public PQuery getQuery() {
39 return query;
40 }
41
42 @Override
43 public String getPatternName() {
44 return query.getFullyQualifiedName();
45 }
46
47 /**
48 * @since 1.6
49 */
50 public Map<PBody, ? extends RecipeTraceInfo> getParentRecipeTracesPerBody() {
51 return parentRecipeTracesPerBody;
52 }
53
54}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.traceability;
10
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Set;
16import java.util.stream.Collectors;
17
18import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
19import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
20import tools.refinery.viatra.runtime.matchers.util.Preconditions;
21import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
22
23/**
24 * A trace marker associating a Rete recipe with a query SubPlan.
25 *
26 * <p> The Rete node represented by the recipe is equivalent to the SubPlan.
27 * <p> Invariant: each variable has at most one index associated with it in the tuple, i.e. no duplicates.
28 */
29public class CompiledSubPlan extends PlanningTrace {
30
31 public CompiledSubPlan(SubPlan subPlan, List<PVariable> variablesTuple,
32 ReteNodeRecipe recipe,
33 Collection<? extends RecipeTraceInfo> parentRecipeTraces) {
34 super(subPlan, variablesTuple, recipe, parentRecipeTraces);
35
36 // Make sure that each variable occurs only once
37 Set<PVariable> variablesSet = new HashSet<PVariable>(variablesTuple);
38 Preconditions.checkState(variablesSet.size() == variablesTuple.size(),
39 () -> String.format(
40 "Illegal column duplication (%s) while the query plan %s was compiled into a Rete Recipe %s",
41 variablesTuple.stream().map(PVariable::getName).collect(Collectors.joining(",")),
42 subPlan.toShortString(), recipe));
43 }
44
45 public CompiledSubPlan(SubPlan subPlan, List<PVariable> variablesTuple,
46 ReteNodeRecipe recipe,
47 RecipeTraceInfo... parentRecipeTraces) {
48 this(subPlan, variablesTuple, recipe, Arrays.asList(parentRecipeTraces));
49 }
50
51}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.traceability;
10
11import java.util.Arrays;
12import java.util.Collection;
13
14import tools.refinery.viatra.runtime.matchers.psystem.PBody;
15import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
16
17/**
18 * The recipe projects the finished results of a {@link PBody} onto the list of parameters.
19 * @author Bergmann Gabor
20 *
21 */
22public class ParameterProjectionTrace extends RecipeTraceInfo implements PatternTraceInfo {
23
24 public ParameterProjectionTrace(PBody body, ReteNodeRecipe recipe,
25 RecipeTraceInfo... parentRecipeTraces) {
26 this(body, recipe, Arrays.asList(parentRecipeTraces));
27 }
28
29 public ParameterProjectionTrace(PBody body, ReteNodeRecipe recipe,
30 Collection<? extends RecipeTraceInfo> parentRecipeTraces) {
31 super(recipe, parentRecipeTraces);
32 this.body = body;
33 }
34
35 PBody body;
36
37 @Override
38 public String getPatternName() {
39 return body.getPattern().getFullyQualifiedName();
40 }
41
42}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.traceability;
10
11/**
12 * One kind of trace marker that merely establishes the pattern for which the node was built.
13 * @author Bergmann Gabor
14 */
15public interface PatternTraceInfo extends TraceInfo {
16 String getPatternName();
17} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.traceability;
10
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.HashMap;
14import java.util.List;
15import java.util.Map;
16
17import tools.refinery.viatra.runtime.matchers.planning.SubPlan;
18import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
19import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
20
21/**
22 * A trace marker associating a Rete recipe with a query SubPlan.
23 *
24 * <p> The recipe may be an auxiliary node;
25 * see {@link CompiledSubPlan} if it represents the entire SubPlan instead.
26 */
27public class PlanningTrace extends RecipeTraceInfo implements PatternTraceInfo {
28
29 protected SubPlan subPlan;
30 protected List<PVariable> variablesTuple;
31 protected Map<PVariable, Integer> posMapping;
32
33 public PlanningTrace(SubPlan subPlan, List<PVariable> variablesTuple,
34 ReteNodeRecipe recipe,
35 Collection<? extends RecipeTraceInfo> parentRecipeTraces) {
36 super(recipe, parentRecipeTraces);
37 this.subPlan = subPlan;
38 this.variablesTuple = variablesTuple;
39
40 this.posMapping = new HashMap<PVariable, Integer>();
41 for (int i = 0; i < variablesTuple.size(); ++i)
42 posMapping.put(variablesTuple.get(i), i);
43 }
44
45 public PlanningTrace(SubPlan subPlan, List<PVariable> variablesTuple,
46 ReteNodeRecipe recipe,
47 RecipeTraceInfo... parentRecipeTraces) {
48 this(subPlan, variablesTuple, recipe, Arrays.asList(parentRecipeTraces));
49 }
50
51 public SubPlan getSubPlan() {
52 return subPlan;
53 }
54
55 @Override
56 public String getPatternName() {
57 return subPlan.getBody().getPattern().getFullyQualifiedName();
58 }
59
60 public List<PVariable> getVariablesTuple() {
61 return variablesTuple;
62 }
63
64 public Map<PVariable, Integer> getPosMapping() {
65 return posMapping;
66 }
67
68 /**
69 * Returns a new clone that reinterprets the same compiled form
70 * as the compiled form of a (potentially different) subPlan.
71 * Useful e.g. if child plan turns out to be a no-op, or when promoting a {@link PlanningTrace} to {@link CompiledSubPlan}.
72 */
73 public CompiledSubPlan cloneFor(SubPlan newSubPlan) {
74 return new CompiledSubPlan(newSubPlan,
75 getVariablesTuple(),
76 getRecipe(),
77 getParentRecipeTracesForCloning());
78 }
79
80} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.traceability;
10
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.List;
16
17import tools.refinery.viatra.runtime.rete.network.Node;
18import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
19
20/**
21 * A trace marker that indicates the recipe for which the node was built.
22 * @author Bergmann Gabor
23 */
24public class RecipeTraceInfo implements TraceInfo {
25 public ReteNodeRecipe getRecipe() {return recipe;}
26 /**
27 * For cloning in case of recursion cut-off points, use {@link #getParentRecipeTracesForCloning()} instead.
28 * @return an unmodifiable view on parent traces, to be constructed before this node (or alongside, in case of recursion)
29 */
30 public List<RecipeTraceInfo> getParentRecipeTraces() {return Collections.unmodifiableList(new ArrayList<>(parentRecipeTraces));}
31 /**
32 * Directly return the underlying collection so that changes to it will be transparent. Use only for recursion-tolerant cloning.
33 * @noreference This method is not intended to be referenced by clients.
34 */
35 public Collection<? extends RecipeTraceInfo> getParentRecipeTracesForCloning() {return parentRecipeTraces;}
36 @Override
37 public Node getNode() {return node;}
38
39 private Node node;
40 ReteNodeRecipe recipe;
41 ReteNodeRecipe shadowedRecipe;
42 Collection<? extends RecipeTraceInfo> parentRecipeTraces;
43
44
45 public RecipeTraceInfo(ReteNodeRecipe recipe, Collection<? extends RecipeTraceInfo> parentRecipeTraces) {
46 super();
47 this.recipe = recipe;
48 this.parentRecipeTraces = parentRecipeTraces; //ParentTraceList.from(parentRecipeTraces);
49 }
50 public RecipeTraceInfo(ReteNodeRecipe recipe, RecipeTraceInfo... parentRecipeTraces) {
51 this(recipe, Arrays.asList(parentRecipeTraces));
52 }
53
54 @Override
55 public boolean propagateToIndexerParent() {return false;}
56 @Override
57 public boolean propagateFromIndexerToSupplierParent() {return false;}
58 @Override
59 public boolean propagateFromStandardNodeToSupplierParent() {return false;}
60 @Override
61 public boolean propagateToProductionNodeParentAlso() {return false;}
62 @Override
63 public void assignNode(Node node) {this.node = node;}
64
65 /**
66 * @param knownRecipe a known recipe that is equivalent to the current recipe
67 */
68 public void shadowWithEquivalentRecipe(ReteNodeRecipe knownRecipe) {
69 this.shadowedRecipe = this.recipe;
70 this.recipe = knownRecipe;
71 }
72
73 /**
74 * Get original recipe shadowed by an equivalent
75 */
76 public ReteNodeRecipe getShadowedRecipe() {
77 return shadowedRecipe;
78 }
79
80
81} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.traceability;
10
11import tools.refinery.viatra.runtime.rete.network.Node;
12
13
14/**
15 * Traces the node back to a purpose for which the node was built,
16 * to explain why the node is there and what it means.
17 * @author Bergmann Gabor
18 */
19public interface TraceInfo {
20 boolean propagateToIndexerParent();
21 boolean propagateFromIndexerToSupplierParent();
22 boolean propagateFromStandardNodeToSupplierParent();
23 boolean propagateToProductionNodeParentAlso();
24
25 void assignNode(Node node);
26 Node getNode();
27}
28// /**
29// * The semantics of the tuples contained in this node.
30// * @return a tuple of correct size representing the semantics of each position.
31// * @post not null
32// */
33// 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.traceability;
10
11import java.util.Collection;
12
13import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe;
14
15// private class AggregatorReferenceIndexTraceInfo extends RecipeTraceInfo {
16// RecipeTraceInfo aggregatorNodeRecipeTrace;
17// public AggregatorReferenceIndexTraceInfo(ProjectionIndexerRecipe recipe,
18// RecipeTraceInfo parentRecipeTrace,
19// RecipeTraceInfo aggregatorNodeRecipeTrace) {
20// super(recipe, parentRecipeTrace);
21// this.aggregatorNodeRecipeTrace = aggregatorNodeRecipeTrace;
22// }
23// public RecipeTraceInfo getAggregatorNodeRecipeTrace() {
24// return aggregatorNodeRecipeTrace;
25// }
26// }
27public class UserRequestTrace extends RecipeTraceInfo {
28 public UserRequestTrace(ReteNodeRecipe recipe,
29 Collection<RecipeTraceInfo> parentRecipeTraces) {
30 super(recipe, parentRecipeTraces);
31 }
32 public UserRequestTrace(ReteNodeRecipe recipe,
33 RecipeTraceInfo... parentRecipeTraces) {
34 super(recipe, parentRecipeTraces);
35 }
36} \ 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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.util;
10
11import java.util.Comparator;
12import java.util.Iterator;
13
14/**
15 * A comparator that compares two iterables based on the lexicographic sorting induced by a comparator on elements.
16 * @author Bergmann Gabor
17 *
18 */
19public class LexicographicComparator<T> implements Comparator<Iterable<? extends T>> {
20
21 final Comparator<T> elementComparator;
22
23 public LexicographicComparator(Comparator<T> elementComparator) {
24 super();
25 this.elementComparator = elementComparator;
26 }
27
28 @Override
29 public int compare(Iterable<? extends T> o1, Iterable<? extends T> o2) {
30 Iterator<? extends T> it1 = o1.iterator();
31 Iterator<? extends T> it2 = o2.iterator();
32
33 boolean has1, has2, bothHaveNext;
34 do {
35 has1 = it1.hasNext();
36 has2 = it2.hasNext();
37 bothHaveNext = has1 && has2;
38 if (bothHaveNext) {
39 T element1 = it1.next();
40 T element2 = it2.next();
41 int elementComparison = elementComparator.compare(element1, element2);
42 if (elementComparison != 0)
43 return elementComparison;
44 }
45 } while (bothHaveNext);
46 if (has1 && !has2) {
47 return +1;
48 } else if (!has1 && has2) {
49 return -1;
50 } else /*if (!has1 && !has2)*/ {
51 return 0;
52 }
53 }
54
55
56
57
58}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.util;
11
12import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider;
13import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext;
14import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy;
15import tools.refinery.viatra.runtime.rete.construction.basiclinear.BasicLinearLayout;
16import tools.refinery.viatra.runtime.rete.construction.quasitree.QuasiTreeLayout;
17import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyCommunicationGroup;
18
19/**
20 * Feature switches.
21 * @author Gabor Bergmann
22 * @noreference
23 */
24public class Options {
25
26 public enum NodeSharingOption {
27 NEVER, // not recommended, patternmatcher leaks possible
28 INDEXER_AND_REMOTEPROXY, ALL
29 }
30
31 public static final NodeSharingOption nodeSharingOption = NodeSharingOption.ALL;
32 public static final boolean releaseOnetimeIndexers = true; // effective only
33 // with
34 // nodesharing
35 // ==NEVER
36
37 public enum InjectivityStrategy {
38 EAGER, LAZY
39 }
40
41 public static final InjectivityStrategy injectivityStrategy = InjectivityStrategy.EAGER;
42
43 public static final boolean enableInheritance = true;
44
45 // public final static boolean useComplementerMask = true;
46
47 public static final boolean employTrivialIndexers = true;
48
49 // public final static boolean synchronous = false;
50
51 public static final int numberOfLocalContainers = 1;
52 public static final int firstFreeContainer = 0; // 0 if head container is
53 // free to contain pattern
54 // bodies, 1 otherwise
55
56 /**
57 * Enable for internal debugging of Rete communication scheme;
58 * catches cases where the topological sort is violated by a message sent "backwards"
59 * @since 1.6
60 */
61 public static final boolean MONITOR_VIOLATION_OF_RETE_NODEGROUP_TOPOLOGICAL_SORTING = false;
62
63 /**
64 * Enable for internal debugging of message delivery in {@link TimelyCommunicationGroup}s;
65 * catches cases when there is a violation of increasing timestamps during message delivery within a group.
66 * @since 2.3
67 */
68 public static final boolean MONITOR_VIOLATION_OF_DIFFERENTIAL_DATAFLOW_TIMESTAMPS = false;
69
70 /**
71 *
72 * @author Gabor Bergmann
73 * @noreference
74 */
75 public enum BuilderMethod {
76 LEGACY, // ONLY with GTASM
77 PSYSTEM_BASIC_LINEAR, PSYSTEM_QUASITREE;
78 /**
79 * @since 1.5
80 */
81 public IQueryPlannerStrategy layoutStrategy(IQueryBackendContext bContext, IQueryBackendHintProvider hintProvider) {
82 switch (this) {
83 case PSYSTEM_BASIC_LINEAR:
84 return new BasicLinearLayout(bContext);
85 case PSYSTEM_QUASITREE:
86 return new QuasiTreeLayout(bContext, hintProvider);
87 default:
88 throw new UnsupportedOperationException();
89 }
90 }
91 }
92
93 public static final BuilderMethod builderMethod =
94 // BuilderMethod.PSYSTEM_BASIC_LINEAR;
95 BuilderMethod.PSYSTEM_QUASITREE;
96
97 public enum FunctionalDependencyOption {
98 OFF,
99 OPPORTUNISTIC
100 }
101 public static final FunctionalDependencyOption functionalDependencyOption =
102 FunctionalDependencyOption.OPPORTUNISTIC;
103
104 public enum PlanTrimOption {
105 OFF,
106 OPPORTUNISTIC
107 }
108 public static final PlanTrimOption planTrimOption =
109 PlanTrimOption.OPPORTUNISTIC;
110
111}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9
10package tools.refinery.viatra.runtime.rete.util;
11
12import java.util.Comparator;
13
14/**
15 * Comparing agent for an ordering. Terminology: the "preferred" item will register as LESS.
16 *
17 * @author Gabor Bergmann
18 *
19 */
20public abstract class OrderingCompareAgent<T> {
21 protected T a;
22 protected T b;
23
24 /**
25 * @param a
26 * @param b
27 */
28 public OrderingCompareAgent(T a, T b) {
29 super();
30 this.a = a;
31 this.b = b;
32 }
33
34 int result = 0;
35
36 protected abstract void doCompare();
37
38 /**
39 * @return the result
40 */
41 public int compare() {
42 doCompare();
43 return result;
44 }
45
46 // COMPARISON HELPERS
47 protected boolean isUnknown() {
48 return result == 0;
49 }
50
51 /**
52 * @pre result == 0
53 */
54 protected boolean consider(int partial) {
55 if (isUnknown())
56 result = partial;
57 return isUnknown();
58 }
59
60 protected boolean swallowBoolean(boolean x) {
61 return x;
62 }
63
64 // PREFERENCE FUNCTIONS
65 protected static int dontCare() {
66 return 0;
67 }
68
69 protected static int preferTrue(boolean b1, boolean b2) {
70 return (b1 ^ b2) ? (b1 ? -1 : +1) : 0;
71 }
72
73 protected static int preferFalse(boolean b1, boolean b2) {
74 return (b1 ^ b2) ? (b2 ? -1 : +1) : 0;
75 }
76
77 protected static <U> int preferLess(Comparable<U> c1, U c2) {
78 return c1.compareTo(c2);
79 }
80
81 protected static <U> int preferLess(U c1, U c2, Comparator<U> comp) {
82 return comp.compare(c1, c2);
83 }
84
85 protected static <U> int preferMore(Comparable<U> c1, U c2) {
86 return -c1.compareTo(c2);
87 }
88 protected static <U> int preferMore(U c1, U c2, Comparator<U> comp) {
89 return -comp.compare(c1, c2);
90 }
91
92}
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 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.util;
10
11import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint;
12import tools.refinery.viatra.runtime.matchers.backend.QueryHintOption;
13import tools.refinery.viatra.runtime.rete.matcher.DRedReteBackendFactory;
14
15/**
16 * Provides key objects (of type {@link QueryHintOption}) for {@link QueryEvaluationHint}s.
17 * @author Gabor Bergmann
18 * @since 1.5
19 */
20public final class ReteHintOptions {
21
22 private ReteHintOptions() {/*Utility class constructor*/}
23
24 public static final QueryHintOption<Boolean> useDiscriminatorDispatchersForConstantFiltering =
25 hintOption("useDiscriminatorDispatchersForConstantFiltering", true);
26
27 public static final QueryHintOption<Boolean> prioritizeConstantFiltering =
28 hintOption("prioritizeConstantFiltering", true);
29
30 public static final QueryHintOption<Boolean> cacheOutputOfEvaluatorsByDefault =
31 hintOption("cacheOutputOfEvaluatorsByDefault", true);
32
33 /**
34 * The incremental query evaluator backend can evaluate recursive patterns.
35 * However, by default, instance models that contain cycles are not supported with recursive queries
36 * and can lead to incorrect query results.
37 * Enabling Delete And Rederive (DRED) mode guarantees that recursive query evaluation leads to correct results in these cases as well.
38 *
39 * <p> As DRED may diminish the performance of incremental maintenance, it is not enabled by default.
40 * @since 1.6
41 * @deprecated Use {@link DRedReteBackendFactory} instead of setting this option to true.
42 */
43 @Deprecated
44 public static final QueryHintOption<Boolean> deleteRederiveEvaluation =
45 hintOption("deleteRederiveEvaluation", false);
46
47 /**
48 * This hint allows the query planner to take advantage of "weakened alternative" suggestions of the meta context.
49 * For instance, enumerable unary type constraints may be substituted with a simple type filtering where sufficient.
50 *
51 * @since 1.6
52 */
53 public static final QueryHintOption<Boolean> expandWeakenedAlternativeConstraints =
54 hintOption("expandWeakenedAlternativeConstraints", true);
55
56 // internal helper for conciseness
57 private static <T> QueryHintOption<T> hintOption(String hintKeyLocalName, T defaultValue) {
58 return new QueryHintOption<>(ReteHintOptions.class, hintKeyLocalName, defaultValue);
59 }
60}