-
Notifications
You must be signed in to change notification settings - Fork 176
feat: containing/containedIn/intersecting joiners #2022
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
--- Co-authored-by: Geoffrey De Smet <gds.geoffrey.de.smet@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request introduces three new collection-based joiners (containing, containedIn, and intersecting) to provide significant performance improvements through efficient indexing. The PR also includes several internal refactorings: renaming getElement() to element(), isEmpty() to isRemovable(), and KeyRetriever to KeyUnpacker.
Changes:
- Added new joiner types:
containing,containedIn, andintersectingwith comprehensive API support across all joiner arities - Implemented specialized indexers (ContainingIndexer, ContainedInIndexer, IntersectingIndexer) for efficient query performance
- Refactored internal APIs for better consistency and clarity
Reviewed changes
Copilot reviewed 44 out of 44 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc | Documents new joiner types |
| core/src/main/java/ai/timefold/solver/core/api/score/stream/Joiners.java | Adds public API for containing, containedIn, and intersecting joiners across all arities |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/joiner/JoinerType.java | Adds CONTAINING, CONTAINED_IN, INTERSECTING enum values with flip logic |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/ContainingIndexer.java | New indexer implementation for containing joiner |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/ContainedInIndexer.java | New indexer implementation for containedIn joiner |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/IntersectingIndexer.java | New indexer implementation for intersecting joiner |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/IndexerFactory.java | Updated to create new indexer types |
| core/src/main/java/ai/timefold/solver/core/impl/util/CompositeListEntry.java | New record for storing tuples in multiple downstream indexers |
| core/src/main/java/ai/timefold/solver/core/impl/util/ListEntry.java | Simplified interface (removed isRemoved, renamed getElement to element) |
| core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/KeyUnpacker.java | Renamed from KeyRetriever |
| Test files | Updated tests for new joiners and refactored naming |
Comments suppressed due to low confidence (2)
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/EqualIndexer.java:112
- This method overrides Indexer.get; it is advisable to add an Override annotation.
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/EqualIndexer.java:103 - This method overrides Indexer.iterator; it is advisable to add an Override annotation.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc
Outdated
Show resolved
Hide resolved
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/joiner/JoinerType.java
Outdated
Show resolved
Hide resolved
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/IntersectingIndexer.java
Outdated
Show resolved
Hide resolved
core/src/main/java/ai/timefold/solver/core/impl/util/CompositeListEntry.java
Outdated
Show resolved
Hide resolved
core/src/main/java/ai/timefold/solver/core/impl/util/ElementAwareLinkedList.java
Show resolved
Hide resolved
TomCools
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs look good to me
Christopher-Chianelli
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have at least 1 test that go through the ScoreDirector that uses these Joiners. Additionally, I do not think these Joiners will work if the backing collection changes (such as a InverseShadowVariable) given the same size assertions in the code (IIRC, everything is updated only when calculateScore is called, and not at before/after variable change, so the indexer will encounter a different sized collection if a shadow variable changes).
| /** | ||
| * See {@link EqualIndexer} for explanation of the parameters. | ||
| */ | ||
| private final Map<Key_, Indexer<T>> downstreamIndexerMap = new HashMap<>(16, 0.5f); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These HashMap parameters were found after testing IIRC. Might be better to have them in CollectionUtils so they don't need to be copied and pasted everywhere.
| this.modifyKeyUnpacker = Objects.requireNonNull((KeyUnpacker<KeyCollection_>) keyUnpacker); | ||
| this.queryKeyUnpacker = Objects.requireNonNull(keyUnpacker); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two fields always point to the same thing (but with different generics)?
| throw new IllegalStateException(""" | ||
| Impossible state: the tuple (%s) with composite key (%s) has a different number of children (%d) \ | ||
| than the index key collection size (%d).""" | ||
| .formatted(entry, modifyCompositeKey, children.size(), indexKeyCollection.size())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want to add to the Javadoc using variable collection properties such as PlanningListVariable or InverseShadowVariable is not allowed (since those will trigger this exception as they change size).
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/IndexerFactory.java
Outdated
Show resolved
Hide resolved
| this.modifyKeyUnpacker = Objects.requireNonNull((KeyUnpacker<KeyCollection_>) keyUnpacker); | ||
| this.queryKeyUnpacker = Objects.requireNonNull((KeyUnpacker<KeyCollection_>) keyUnpacker); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two fields will always have the exact same value (with the exact same generics)
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/IntersectingIndexer.java
Outdated
Show resolved
Hide resolved
|
Moving back to draft, as there are score corruptions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 44 out of 44 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (2)
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/EqualIndexer.java:112
- This method overrides Indexer.get; it is advisable to add an Override annotation.
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/EqualIndexer.java:103 - This method overrides Indexer.iterator; it is advisable to add an Override annotation.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/ContainingAnyOfIndexer.java
Show resolved
Hide resolved
docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc
Outdated
Show resolved
Hide resolved
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/KeyUnpacker.java
Outdated
Show resolved
Hide resolved
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/IndexerFactory.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After a conversation with Geoff, we'll do the following:
- The joiners are removed from public API, therefore they can not be used.
- They are also removed from the documentation. There is no public trace of them existing.
- But they are being merged; Geoffrey will eventually get to finishing this work, but we want to avoid further merge conflicts until he does.
This entire piece of work will be re-reviewed as a whole, once the fixes for the score corruptions have been made. I'm leaving all the remaining comments here open, so that during the next round of review, we know what's left to resolve.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 45 out of 45 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/EqualIndexer.java:112
- This method overrides Indexer.get; it is advisable to add an Override annotation.
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/EqualIndexer.java:103 - This method overrides Indexer.iterator; it is advisable to add an Override annotation.
|


Before
After (bigO order of magnitude more scalable)
This is mostly a work of @ge0ffrey, I just rebased it and smoothened some rough edges.
Replaces #1999.