Repository navigation
feat!: run partitioned_search in the framework - #200
Merged
Merged
Conversation
`Director::fork_for_solution` builds an independent director of the same kind over a given solution: the constraint set is built for that solution with the recorded constructor, and the descriptor, weight mode, entity counter and score assertion are shared with the source. Unlike `fork_replica` it neither clones the working solution nor initializes the incremental state, so a sub-problem such as one partition of partitioned search initializes on the thread that solves it. Directors without a constraint-set constructor return `None`.
`PartitionedSearchPhase` solved its partitions inline. The pass — split, solve each partition in its own child scope on the resolved thread count, merge — now lives in `solve_partitioned`, which takes the partitioner, the thread and logging settings, a director maker and the child-phase runner, so the configured runtime can run the same pass. Partition directors are now built on the solving thread, from the parent's director, before the partitions are dispatched; only `Send` directors cross to the worker threads, and each partition still initializes its score on the thread that solves it. `log_progress` now also logs the partition and thread counts at the start of a pass.
A `partitioned_search` phase only looked up a user closure registered with `partitioned_phase` and ran whatever phase it built, so `child_phases` was validated and never run, and `thread_count` and `log_progress` took effect only if the closure forwarded them. The framework now runs the phase. A search declaration registers just the partitioner, with `SearchBuilder::partitioner(name, |context| partitioner)`; `partitioner = "name"` selects it. Each partition is solved by a runner compiled from the parent configuration with `child_phases` as its phases, an empty list meaning the default phases, over a director forked from the solve's own with `Director::fork_for_solution`, on `thread_count` threads, with `log_progress` logging the pass. The graph compiler compiles every child configuration up front, so a child phase that cannot compile fails the solve with its `phases[i].partitioned_search.child_phases[j]` path. A child runner borrows its parent's declaration, so a partitioned phase nested in `child_phases` compiles to the same runner type, and extension registries are `Send + Sync` because partition runners compile on the threads that solve them. BREAKING CHANGE: `SearchBuilder::partitioned_phase` is removed. Register a `SolutionPartitioner` with `SearchBuilder::partitioner` instead; the framework builds and runs the phase from the `partitioned_search` configuration.
A `partitioned_search` phase's `termination` was validated and never applied: the runtime ran the phase without it. It now bounds the partitions. Its time limit, counted from the phase's start, holds for every partition, and its step count limit for each partition's solve. A partition that reaches either ends like a phase: it keeps its best solution, the partitions are merged, and the solve goes on to the next phase instead of terminating. A bound made inside a partition chains to the enclosing one. A partition sees only its part of the solution, so `best_score_limit`, `unimproved_step_count_limit` and `unimproved_seconds_spent_limit` on a partitioned phase are rejected at compile time with their key path. Each partition's configuration carries the earliest of the solve's and the phase's time and step count limits, so the default local search runs in a partition exactly when something bounds it there. Closes #199. BREAKING CHANGE: a `partitioned_search` phase whose `termination` sets `best_score_limit`, `unimproved_step_count_limit` or `unimproved_seconds_spent_limit` is a configuration error.
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.
Closes #199. Base
main@958303a4, four commits:5f143255feat(scoring): fork a director over another solution (Director::fork_for_solution)0c470c01refactor(partitioned): run a partition pass through one shared function11a40691feat(solver)!: run partitioned_search in the framework (SearchBuilder::partitionerreplacespartitioned_phase;child_phasescompiled and run,thread_count,log_progress)6a9f27c9feat(partitioned)!: bound partitions by the phase terminationGate on
6a9f27c9(fmt, clippy, doc-names file-length facade-reachability public-api deps, rustdoc, examples, workspace all-feature tests, doctests): EXIT 0, 1554 passed, 0 failed, 69 result lines. Slice 1: scoring clippy/rustdoc/doc-names/tests 349 passed. Slice 3 tree: solver, facade, config and scoring tests 1250 passed, 0 failed.Mutation checks, each failing its test: child phases not run;
thread_countignored;log_progressignored; child configuration not compiled up front; phase termination not passed; a reached bound treated as a solve stop; merged-score limits not rejected.