fix: restore support for null values in sortable value ranges#2238
fix: restore support for null values in sortable value ranges#2238zepfred wants to merge 1 commit intoTimefoldAI:mainfrom
Conversation
|
There was a problem hiding this comment.
Pull request overview
This PR adds coverage for scenarios where planning variables (basic and list) allow unassigned values while also using value sorting, ensuring the “unassigned/null” option is preserved through sorting and during construction heuristic solving.
Changes:
- Fix
NullAllowingValueRange.sort(...)to keep the null-allowing wrapper after sorting. - Add new test domain solution/entity pairs for sortable entities with
allowsUnassigned/allowsUnassignedValues. - Extend construction heuristic tests to verify solvers can prefer leaving variables unassigned when constraints penalize assignments.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| core/src/main/java/ai/timefold/solver/core/impl/domain/valuerange/NullAllowingValueRange.java | Ensures sorting retains the null-allowing wrapper, keeping the unassigned option available. |
| core/src/test/java/ai/timefold/solver/core/impl/domain/valuerange/NullAllowingValueRangeTest.java | Updates assertions to reflect null being present after sorting. |
| core/src/test/java/ai/timefold/solver/core/impl/constructionheuristic/DefaultConstructionHeuristicPhaseTest.java | Adds constraint-stream-based tests verifying CH can keep basic/list variables unassigned when optimal. |
| core/src/test/java/ai/timefold/solver/core/testdomain/unassignedvar/sort/TestdataAllowsUnassignedSortableSolution.java | New test solution for basic sortable entity with unassigned variable support. |
| core/src/test/java/ai/timefold/solver/core/testdomain/unassignedvar/sort/TestdataAllowsUnassignedSortableEntity.java | New planning entity with @PlanningVariable(allowsUnassigned = true) and comparator sorting. |
| core/src/test/java/ai/timefold/solver/core/testdomain/list/unassignedvar/sort/TestdataAllowsUnassignedListSortableSolution.java | New test solution for list-variable sortable entity with unassigned values support. |
| core/src/test/java/ai/timefold/solver/core/testdomain/list/unassignedvar/sort/TestdataAllowsUnassignedListSortableEntity.java | New planning entity with @PlanningListVariable(allowsUnassignedValues = true) and comparator sorting. |
triceo
left a comment
There was a problem hiding this comment.
LGTM after comments are resolved.
| .withConstraintProviderClass(PenalizeAssignedConstraintProvider.class) | ||
| .withPhases(new ConstructionHeuristicPhaseConfig() | ||
| .withTerminationConfig(new TerminationConfig().withStepCountLimit(3))); | ||
| solverConfig.getScoreDirectorFactoryConfig().setEasyScoreCalculatorClass(null); |
There was a problem hiding this comment.
Please remove this line.
If PlannerTestUtils.buildSolverConfig is forcing you to do this, don't use that method either.
| .withConstraintProviderClass(ListPenalizeAssignedConstraintProvider.class) | ||
| .withPhases(new ConstructionHeuristicPhaseConfig() | ||
| .withTerminationConfig(new TerminationConfig().withStepCountLimit(3))); | ||
| solverConfig.getScoreDirectorFactoryConfig().setEasyScoreCalculatorClass(null); |
| solverConfig.getScoreDirectorFactoryConfig().setEasyScoreCalculatorClass(null); | ||
| var problem = TestdataAllowsUnassignedListSortableSolution.generateSolution(1, 1, false); | ||
| var solution = PlannerTestUtils.solve(solverConfig, problem); | ||
| assertThat(solution).isNotNull(); |
There was a problem hiding this comment.
Arguably not necessary; not sure why we do this so often. Solution can never be null.
| } | ||
|
|
||
| Constraint penalizeAssigned(ConstraintFactory constraintFactory) { | ||
| return constraintFactory.forEach(TestdataAllowsUnassignedSortableEntity.class) |
There was a problem hiding this comment.
This will skip every null value. Shouldn't this be forEachIncludingUnassigned?
Otherwise the subsequent filter is useless.



No description provided.