perf(constraint-streams): implement runtime node deactivation#2337
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces runtime node deactivation to Bavet-based Constraint Streams (and related node networks), allowing the engine to identify and skip propagating through nodes that provably cannot produce any tuples for a given working solution, improving performance while keeping behavior safe.
Changes:
- Added an
ActivitySupportinitialization/activation protocol and implemented activity propagation across tuple lifecycles and nodes. - Reworked node network/session infrastructure (
AbstractBavetNodeNetwork,ConstraintStreamsBavetNodeNetwork, etc.) to trim inactive propagators after initialization. - Added focused unit tests and documentation describing runtime deactivation behavior; removed the old node-network visualization implementation.
Reviewed changes
Copilot reviewed 62 out of 62 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/src/modules/ROOT/pages/constraints-and-score/performance.adoc | Documents runtime constraint deactivation when constraints are provably irrelevant. |
| core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/IfExistsUniNodeActivityTest.java | Verifies activation rules for ifExists/ifNotExists nodes. |
| core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/ForEachUniNodeActivityTest.java | Verifies ForEach node activation behavior based on inserted facts and downstream activity. |
| core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/ConcatUniUniNodeActivityTest.java | Verifies concat activation semantics when one/both sides are empty. |
| core/src/test/java/ai/timefold/solver/core/impl/bavet/common/SingleInputNodeActivityTest.java | Verifies default single-input activation forwarding/behavior. |
| core/src/test/java/ai/timefold/solver/core/impl/bavet/bi/JoinBiNodeActivityTest.java | Verifies join deactivation when either side cannot produce tuples. |
| core/src/test/java/ai/timefold/solver/core/impl/bavet/BavetNodeDeactivationTest.java | End-to-end tests asserting chains deactivate/activate based on solution contents. |
| core/src/test/java/ai/timefold/solver/core/api/solver/SolutionManagerTest.java | Removes tests for node-network visualization API that was removed. |
| core/src/main/java/module-info.java | Exports additional internal packages to the enterprise module. |
| core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolutionManager.java | Removes node-network visualization method and related dependencies. |
| core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetForEachUniConstraintStream.java | Adjusts generic handling and simplifies equals implementation. |
| core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/ConstraintStreamsBavetNodeNetwork.java | Adds node-network implementation that can log deactivated constraints post-initialization. |
| core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/common/Scorer.java | Makes scorer implement activity so constraints can be marked inactive when upstream can’t produce. |
| core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/common/ConstraintNodeBuildHelper.java | Tracks scorers/profile IDs and builds ConstraintStreamsBavetNodeNetwork. |
| core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/common/BavetPrecomputeBuildHelper.java | Migrates precompute to the new node-network abstraction. |
| core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraintSessionFactory.java | Builds the new constraint-streams node network and integrates profiling registration. |
| core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraintSession.java | Uses the new node network type and forwards profiling summarization. |
| core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraintFactory.java | Updates javadoc references after session factory signature change. |
| core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java | Removes visualization consumer plumbing from session creation. |
| core/src/main/java/ai/timefold/solver/core/impl/neighborhood/stream/enumerating/uni/AbstractForEachEnumeratingStream.java | Updates imports due to root node type rename/refactor. |
| core/src/main/java/ai/timefold/solver/core/impl/neighborhood/stream/enumerating/DatasetSessionFactory.java | Builds neighborhoods node network using the new abstraction. |
| core/src/main/java/ai/timefold/solver/core/impl/neighborhood/stream/enumerating/DatasetSession.java | Updates session to accept the new node network abstraction. |
| core/src/main/java/ai/timefold/solver/core/impl/neighborhood/stream/enumerating/common/DataNodeBuildHelper.java | Adds neighborhoods node network construction. |
| core/src/main/java/ai/timefold/solver/core/impl/neighborhood/stream/enumerating/common/AbstractDatasetInstance.java | Adds activity methods (currently TODO) to satisfy new tuple lifecycle contract. |
| core/src/main/java/ai/timefold/solver/core/impl/neighborhood/NeighborhoodsBavetNodeNetwork.java | Adds node-network implementation for neighborhoods. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/visual/NodeGraph.java | Removes legacy Graphviz visualization implementation. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/visual/GraphSink.java | Removes legacy visualization helper type. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/visual/GraphEdge.java | Removes legacy visualization helper type. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/PrecomputeUniNode.java | Minor formatting change. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachUnfilteredUniNode.java | Implements root-node activity based on fact presence and downstream activity. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachFilteredUniNode.java | Implements activity based on “saw any input” semantics and downstream activity. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/AbstractForEachUniNode.java | Migrates ForEach root nodes to the new root-node base type. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/NodeNetwork.java | Removes old node-network record in favor of the new abstraction. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/TupleLifecycle.java | Makes tuple lifecycles activity-aware and updates profiling wrapper introspection. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/RightTupleLifecycleImpl.java | Adds activity tracking and delegation behavior for right lifecycles. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/RightTupleLifecycle.java | Extends right lifecycle contract with activity initialization and querying. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/RecordingTupleLifecycle.java | Implements activity methods (always active) to satisfy new lifecycle contract. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/ProfilingTupleLifecycle.java | Converts record to class and adds activity tracking delegation. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/LeftTupleLifecycleImpl.java | Adds activity tracking and delegation behavior for left lifecycles. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/LeftTupleLifecycle.java | Extends left lifecycle contract with activity initialization and querying. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/ConditionalTupleLifecycle.java | Adds activity tracking so conditional lifecycles can be trimmed when upstream can’t produce. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/AggregatedTupleLifecycle.java | Adds activity-aware downstream filtering/finalization. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/ActivitySupport.java | Introduces the activity initialization/activation protocol. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/RecordAndReplayPropagator.java | Adapts to new node-network abstraction and exposes tuple-production capability. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/Propagator.java | Formatting-only change. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/ProfilingPropagator.java | Formatting-only change. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/InnerConstraintProfiler.java | Adds node-graph registration hook (replacing removed NodeGraph builder usage). |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractTwoInputNode.java | Centralizes two-input initialization and activation logic. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractSingleInputNode.java | Introduces default single-input activation forwarding logic. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractRootNode.java | Replaces root-node interface with abstract base class implementing activity. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractPrecomputeNode.java | Implements activity for precompute roots based on record/replay capability. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractNodeBuildHelper.java | Removes old NodeNetwork building and adjusts layering logic for new node type hierarchy. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractNode.java | Seals node hierarchy to enforce known node kinds for activation switching. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractMapNode.java | Migrates map nodes to the new single-input base class. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractJoinNode.java | Hooks join nodes into centralized two-input activation logic. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIfExistsNode.java | Implements can-produce logic consistent with ifExists/ifNotExists semantics. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractGroupNode.java | Migrates group nodes to the new single-input base class. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractFlattenNode.java | Migrates flatten nodes to the new single-input base class. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractConcatNode.java | Implements concat’s “either side can produce” activation logic. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/AbstractSession.java | Makes sessions generic over node-network type and exposes the built network. |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/AbstractBavetNodeNetwork.java | Adds new node-network abstraction including activation trimming and settle orchestration. |
| core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintRef.java | Adds toString override for clearer logging/debugging output. |
# Conflicts: # core/src/main/java/ai/timefold/solver/core/impl/bavet/common/RecordAndReplayPropagator.java
# Conflicts: # core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/AbstractForEachUniNode.java
Christopher-Chianelli
approved these changes
Jun 8, 2026
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


While I was there, I moved the hidden network visualization feature to Enterprise, and exposed it publicly as part of constraint profiling.
Enterprise counterpart: https://github.com/TimefoldAI/timefold-solver-enterprise/pull/662