Skip to content

perf: optimize value range handling of basic variable neighborhoods - #2814

Closed
triceo wants to merge 2 commits into
TimefoldAI:mainfrom
triceo:neighborhoods-value-range-draws
Closed

triceo wants to merge 2 commits into
TimefoldAI:mainfrom
triceo:neighborhoods-value-range-draws

Conversation

@triceo

@triceo triceo commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Before this PR, the swap code would select all pairs, and only then knock out those who did not fill the other's value ranges. This was super-slow, and the move selectors do it in a much smarter way, which the neighborhoods now use as well - they only select left, and then look for the right partner in the static value ranges.

This changes the distribution of moves selected by some of the move providers - for example, longer sublists now have a significantly smaller chance of being picked, but that is not necessarily bad. (Long lists are bad for performance overall, and typically it is a good idea to limit the size of those lists.) That is a small price we have to pay for massive performance bumps - the decisions made now in terms of distribution are identical to move selectors, where possible.

This represents a speedup of 2x to 100x depending on dataset.
Validated with ESS, which now (on average across multiple seeds) reaches same or better results on all measured datasets, both in terms of score and move speed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical issues can prematurely stop move generation, return incompatible entity types, and cause quadratic memory growth during swap candidate lookup.

4 open issues
What changed in this PR

This PR optimizes basic-variable neighborhoods by drawing values and swap partners from applicable value ranges, reducing rejected candidates.

Changes:

  • Adds range-driven iterators for assign, change, swap, and pillar-swap moves.
  • Adds cached lookups for movable entities that accept a value.
  • Expands validity, pinning, and sampling tests, and documents range-based candidate selection.
File Description
docs/​src/​modules/​ROOT/​pages/​optimization-algorithms/​neighborhoods.adoc Documents range-based candidate selection.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​testdomain/​valuerange/​entityproviding/​pinned/​TestdataPinnedEntityProvidingSolution.java Adds a solution fixture with entity ranges and pinning.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​testdomain/​valuerange/​entityproviding/​pinned/​TestdataPinnedEntityProvidingEntity.java Adds an entity-range fixture with pinning.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​testdomain/​inheritance/​entity/​single/​baseannotated/​classes/​pinned/​TestdataExtendedPinnedSolution.java Adds an inheritance and pinning solution fixture.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​testdomain/​inheritance/​entity/​single/​baseannotated/​classes/​pinned/​TestdataExtendedPinnedEntity.java Adds subclass-specific pinning to a test entity.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​SwapMoveProviderTest.java Tests valid partners, pinning, and sampling frequency.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​SubPillarSwapMoveProviderTest.java Tests subpillar sampling and move validity.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​PillarSwapMoveProviderTest.java Tests pillar sampling frequency and validity.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​MoveProviderAssertions.java Adds shared validity and sampling assertions.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​ChangeMoveProviderTest.java Tests range-limited changes and sampling.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​AssignMoveProviderTest.java Tests non-null assignments and sampling.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​impl/​move/​MoveDirectorTest.java Tests candidate lookup and pinning behavior.
core/​src/​test/​java/​ai/​timefold/​solver/​core/​impl/​move/​builtin/​LeftDrivenMoveIteratorTest.java Tests draw-budget overflow handling.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​SolutionView.java Adds the movable-entity range lookup API.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​SwapMoveProvider.java Uses the range-driven swap iterator.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​SubPillarSwapMoveProvider.java Narrows candidate keys using value ranges.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​PillarSwapMoveProvider.java Uses keyed pillar data and a dedicated iterator.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​ChangeMoveProvider.java Draws changes from each entity’s range.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​preview/​api/​move/​builtin/​AssignMoveProvider.java Draws assignments from each entity’s range.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​score/​director/​ValueRangeState.java Supplies movability filtering to range indexes.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​score/​director/​ValueRangeManager.java Caches movable entities and handles subclass pins.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​neighborhood/​stream/​DefaultMoveStreamFactory.java Removes filters replaced by range-driven selection.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​move/​MoveDirector.java Implements movable-entity range lookup.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​move/​builtin/​ValueRangeChangeMoveIterator.java Adds direct value-range sampling.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​move/​builtin/​SwapMoveIterator.java Adds range-filtered swap partner selection.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​move/​builtin/​PillarWithRanges.java Extracts the cached pillar-and-ranges record.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​move/​builtin/​PillarSwapMoveIterator.java Adds candidate-based pillar selection with size correction.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​move/​builtin/​MoveProviderUtil.java Selects the smallest range-based candidate list.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​move/​builtin/​LeftDrivenMoveIterator.java Adds shared bounded random-draw logic.
core/​src/​main/​java/​ai/​timefold/​solver/​core/​impl/​heuristic/​selector/​common/​ReachableValues.java Caches movable entities for each value.

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Rebuilding and deduplicating candidate keys on every move draw replaces expected constant-time probing with linear work and repeated allocations, creating a performance regression.

No open issues 🎉

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 11, 2026 10:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Right-partner selection rebuilds candidate keys on every draw, introducing linear work and allocations where rejection sampling previously had expected constant cost.

No open issues 🎉

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 11, 2026 10:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The move-selection hot path rebuilds candidate keys on every draw and retry, introducing linear scans and allocations that can cause a significant performance regression.

No open issues 🎉

Review effort: Balanced

@triceo triceo self-assigned this Nov 11, 2026
@triceo triceo modified the milestone: v2.9.0-rc-1 Nov 11, 2026
@triceo

triceo commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Will be replaced by another PR.

@triceo triceo closed this Nov 11, 2026
@triceo
triceo deleted the neighborhoods-value-range-draws branch November 11, 2026 15:40

This branch was successfully deployed

1 active and 1 inactive deployments
documentation (preview) — adbb86d6 Deployed Nov 11, 2026 by triceo via Build Documentation #880
internal — adbb86d6 Deployed Nov 11, 2026 by triceo via approval_required #5279
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants