-
Notifications
You must be signed in to change notification settings - Fork 237
docs: new nav structure #2634
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
Open
TomCools
wants to merge
11
commits into
TimefoldAI:main
Choose a base branch
from
TomCools:docs/new-nav-structure
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
docs: new nav structure #2634
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
eef8997
docs: new nav structure
TomCools 24b706c
docs: try more compact nav structure
TomCools 2ddd9a2
docs: adjust menu structure
TomCools b99363c
Apply batched suggestions from code review
TomCools 2d82a15
docs: shorter section title
TomCools 5215f7e
docs: separate out moves
TomCools 152c087
docs: set optimization algorithms in the correct subheading
TomCools 146ca06
docs: fix custom move header level
TomCools b69d3c5
docs: remove introduction from the nav
TomCools a56e9dd
docs: improvements
TomCools d7a1fa6
docs: fix link
TomCools File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
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
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
241 changes: 241 additions & 0 deletions
241
docs/src/modules/ROOT/pages/optimization-algorithms/custom-moves.adoc
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,241 @@ | ||
| [#customMovesIntroduction] | ||
|
TomCools marked this conversation as resolved.
|
||
| = Custom moves | ||
| :doctype: book | ||
| :sectnums: | ||
| :icons: font | ||
|
|
||
| Instead of using the generic ``Move``s (such as ``ChangeMove``) you can also implement your own ``Move``. | ||
| Generic and custom ``MoveSelector``s can be xref:optimization-algorithms/move-selector-reference.adoc#combiningMultipleMoveSelectors[combined] as desired. | ||
|
|
||
| A custom `Move` can be tailored to work to the advantage of your constraints. | ||
| For example, in examination scheduling, changing the period of an exam A | ||
| would also change the period of all the other exams that need to coincide with exam A. | ||
|
|
||
| A custom `Move` is far more work to implement and much harder to avoid bugs than a generic ``Move``. | ||
| After implementing a custom ``Move``, turn on `environmentMode` ``TRACED_FULL_ASSERT`` to check for score corruptions. | ||
|
|
||
| For information on the `Move` interface, check out the | ||
| xref:optimization-algorithms/neighborhoods.adoc#neighborhoodsMove[Move Anatomy] section of the Neighborhoods chapter. | ||
| Even though Neighborhoods is an entirely different mechanism than move selectors, | ||
| they share the `Move` interface and therefore the same custom `Move` can be used in both mechanisms. | ||
|
|
||
|
|
||
| [#generatingCustomMoves] | ||
| == Generating custom moves | ||
|
|
||
| Now, let's generate instances of this custom ``Move`` class. | ||
| There are 2 ways: | ||
|
|
||
| [#moveListFactory] | ||
| === `MoveListFactory`: the easy way to generate custom moves | ||
|
|
||
| The easiest way to generate custom moves is by implementing the interface ``MoveListFactory``: | ||
|
|
||
| [source,java,options="nowrap"] | ||
| ---- | ||
| public interface MoveListFactory<Solution_> { | ||
|
|
||
| List<Move> createMoveList(Solution_ solution); | ||
|
|
||
| } | ||
| ---- | ||
|
|
||
| Simple configuration (which can be nested in a `unionMoveSelector` just like any other ``MoveSelector``): | ||
|
|
||
| [source,xml,options="nowrap"] | ||
| ---- | ||
| <moveListFactory> | ||
| <moveListFactoryClass>...MyMoveFactory</moveListFactoryClass> | ||
| </moveListFactory> | ||
| ---- | ||
|
|
||
| Advanced configuration: | ||
|
|
||
| [source,xml,options="nowrap"] | ||
| ---- | ||
| <moveListFactory> | ||
| ... <!-- Normal moveSelector properties --> | ||
| <moveListFactoryClass>...MyMoveFactory</moveListFactoryClass> | ||
| <moveListFactoryCustomProperties> | ||
| ...<!-- Custom properties --> | ||
| </moveListFactoryCustomProperties> | ||
| </moveListFactory> | ||
| ---- | ||
|
|
||
| Because the `MoveListFactory` generates all moves at once in a ``List<Move>``, | ||
| it does not support `cacheType` ``JUST_IN_TIME``. | ||
| Therefore, `moveListFactory` uses `cacheType` ``STEP`` by default and it scales badly. | ||
|
|
||
| To configure values of a `MoveListFactory` dynamically in the solver configuration | ||
| (so the xref:running-timefold-solver/benchmarking-and-tweaking.adoc#benchmarker[Benchmarker] can tweak those parameters), | ||
| add the `moveListFactoryCustomProperties` element and use xref:running-timefold-solver/library/configuration.adoc#customPropertiesConfiguration[custom properties]. | ||
|
|
||
| [WARNING] | ||
| ==== | ||
| A custom `MoveListFactory` implementation must ensure that it does not move xref:domain-modeling/modeling-planning-problems.adoc#pinnedPlanningEntities[pinned entities]. | ||
| ==== | ||
|
|
||
|
|
||
| [#moveIteratorFactory] | ||
| === ``MoveIteratorFactory``: generate Custom moves just in time | ||
|
|
||
| Use this advanced form to generate custom moves Just In Time | ||
| by implementing the `MoveIteratorFactory` interface: | ||
|
|
||
| [source,java,options="nowrap"] | ||
| ---- | ||
| public interface MoveIteratorFactory<Solution_> { | ||
|
|
||
| long getSize(ScoreDirector<Solution_> scoreDirector); | ||
|
|
||
| Iterator<Move> createOriginalMoveIterator(ScoreDirector<Solution_> scoreDirector); | ||
|
|
||
| Iterator<Move> createRandomMoveIterator(ScoreDirector<Solution_> scoreDirector, Random workingRandom); | ||
|
Comment on lines
+87
to
+93
|
||
|
|
||
| } | ||
| ---- | ||
|
|
||
| The `getSize()` method must return an estimation of the size. | ||
| It doesn't need to be correct, but it's better too big than too small. | ||
| The `createOriginalMoveIterator` method is called if the `selectionOrder` is `ORIGINAL` or if it is cached. | ||
| The `createRandomMoveIterator` method is called for `selectionOrder` ``RANDOM`` combined with cacheType ``JUST_IN_TIME``. | ||
|
|
||
| [IMPORTANT] | ||
| ==== | ||
| Don't create a collection (array, list, set or map) of ``Move``s when creating the ``Iterator<Move>``: | ||
| the whole purpose of `MoveIteratorFactory` over `MoveListFactory` is to create a `Move` just in time | ||
| in a custom ``Iterator.next()``. | ||
| ==== | ||
|
|
||
| For example: | ||
|
|
||
| [source,java,options="nowrap"] | ||
| ---- | ||
| public class PossibleAssignmentsOnlyMoveIteratorFactory implements MoveIteratorFactory<MyPlanningSolution, MyChangeMove> { | ||
| @Override | ||
| public long getSize(ScoreDirector<MyPlanningSolution> scoreDirector) { | ||
| // In this case, we return the exact size, but an estimate can be used | ||
| // if it too expensive to calculate or unknown | ||
| long totalSize = 0L; | ||
| var solution = scoreDirector.getWorkingSolution(); | ||
| for (MyEntity entity : solution.getEntities()) { | ||
| for (MyPlanningValue value : solution.getValues()) { | ||
| if (entity.canBeAssigned(value)) { | ||
| totalSize++; | ||
| } | ||
| } | ||
| } | ||
| return totalSize; | ||
| } | ||
|
|
||
| @Override | ||
| public Iterator<MyChangeMove> createOriginalMoveIterator(ScoreDirector<MyPlanningSolution> scoreDirector) { | ||
| // Only needed if selectionOrder is ORIGINAL or if it is cached | ||
| var solution = scoreDirector.getWorkingSolution(); | ||
| var entities = solution.getEntities(); | ||
| var values = solution.getValues(); | ||
| // Assumes each entity has at least one assignable value | ||
| var firstEntityIndex = 0; | ||
| var firstValueIndex = 0; | ||
| while (!entities.get(firstEntityIndex).canBeAssigned(values.get(firstValueIndex))) { | ||
| firstValueIndex++; | ||
| } | ||
|
|
||
|
|
||
| return new Iterator<>() { | ||
| int nextEntityIndex = firstEntityIndex; | ||
| int nextValueIndex = firstValueIndex; | ||
|
|
||
| @Override | ||
| public boolean hasNext() { | ||
| return nextEntityIndex < entities.size(); | ||
| } | ||
|
|
||
| @Override | ||
| public MyChangeMove next() { | ||
| var selectedEntity = entities.get(nextEntityIndex); | ||
| var selectedValue = values.get(nextValueIndex); | ||
| nextValueIndex++; | ||
| while (nextValueIndex < values.size() && !selectedEntity.canBeAssigned(values.get(nextValueIndex))) { | ||
| nextValueIndex++; | ||
| } | ||
| if (nextValueIndex >= values.size()) { | ||
| // value list exhausted, go to next entity | ||
| nextEntityIndex++; | ||
| if (nextEntityIndex < entities.size()) { | ||
| nextValueIndex = 0; | ||
| while (nextValueIndex < values.size() && !entities.get(nextEntityIndex).canBeAssigned(values.get(nextValueIndex))) { | ||
| // Assumes each entity has at least one assignable value | ||
| nextValueIndex++; | ||
| } | ||
| } | ||
| } | ||
| return new MyChangeMove(selectedEntity, selectedValue); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| @Override | ||
| public Iterator<MyChangeMove> createRandomMoveIterator(ScoreDirector<MyPlanningSolution> scoreDirector, | ||
| Random workingRandom) { | ||
| // Not needed if selectionOrder is ORIGINAL or if it is cached | ||
| var solution = scoreDirector.getWorkingSolution(); | ||
| var entities = solution.getEntities(); | ||
| var values = solution.getValues(); | ||
|
|
||
| return new Iterator<>() { | ||
| @Override | ||
| public boolean hasNext() { | ||
| return !entities.isEmpty(); | ||
| } | ||
|
|
||
| @Override | ||
| public MyChangeMove next() { | ||
| var selectedEntity = entities.get(workingRandom.nextInt(entities.size())); | ||
| var selectedValue = values.get(workingRandom.nextInt(values.size())); | ||
| while (!selectedEntity.canBeAssigned(selectedValue)) { | ||
| // This assumes there at least one value that can be assigned to the selected entity | ||
| selectedValue = values.get(workingRandom.nextInt(values.size())); | ||
| } | ||
| return new MyChangeMove(selectedEntity, selectedValue); | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| ---- | ||
|
|
||
| [NOTE] | ||
| ==== | ||
| The same effect can also be achieved using xref:optimization-algorithms/move-selector-reference.adoc#filteredSelection[filtered selection]. | ||
| ==== | ||
|
|
||
| Simple configuration (which can be nested in a `unionMoveSelector` just like any other ``MoveSelector``): | ||
|
|
||
| [source,xml,options="nowrap"] | ||
| ---- | ||
| <moveIteratorFactory> | ||
| <moveIteratorFactoryClass>...</moveIteratorFactoryClass> | ||
| </moveIteratorFactory> | ||
| ---- | ||
|
|
||
| Advanced configuration: | ||
|
|
||
| [source,xml,options="nowrap"] | ||
| ---- | ||
| <moveIteratorFactory> | ||
| ... <!-- Normal moveSelector properties --> | ||
| <moveIteratorFactoryClass>...</moveIteratorFactoryClass> | ||
| <moveIteratorFactoryCustomProperties> | ||
| ...<!-- Custom properties --> | ||
| </moveIteratorFactoryCustomProperties> | ||
| </moveIteratorFactory> | ||
| ---- | ||
|
|
||
| To configure values of a `MoveIteratorFactory` dynamically in the solver configuration | ||
| (so the xref:running-timefold-solver/benchmarking-and-tweaking.adoc#benchmarker[Benchmarker] can tweak those parameters), | ||
| add the `moveIteratorFactoryCustomProperties` element and use xref:running-timefold-solver/library/configuration.adoc#customPropertiesConfiguration[custom properties]. | ||
|
|
||
| [WARNING] | ||
| ==== | ||
| A custom `MoveIteratorFactory` implementation must ensure that it does not move xref:domain-modeling/modeling-planning-problems.adoc#pinnedPlanningEntities[pinned entities]. | ||
| ==== | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.