document solvers

main
Matthew Butterick 3 years ago
parent 0274fb999f
commit c562ddbdb7

@ -576,7 +576,7 @@
(assign-val prob name val)]))))))
(define/contract (min-conflicts-solver prob [max-steps 100])
((csp?) (integer?) . ->* . generator?)
((csp?) (exact-positive-integer?) . ->* . generator?)
(generator ()
(for ([thread-count (or (current-thread-count) 1)]) ; todo: what is ideal thread count?
(make-min-conflcts-thread prob thread-count max-steps))

@ -394,7 +394,7 @@ Current inference rule used by the solver. If @racket[#false], solver uses @rack
}
@defparam[current-solver val (or/c #false procedure?) #:value #f]{
Current solver algorithm used to solve the CSP. If @racket[#false], CSP will use a backtracking solver.
Current solver algorithm used to solve the CSP. If @racket[#false], CSP will use @racket[backtracking-solver].
}
@defparam[current-decompose val (or/c #false procedure?) #:value #t]{
@ -402,7 +402,7 @@ Whether CSP will be decomposed into independent subproblems (if possible), becau
}
@defparam[current-thread-count val (or/c #false natural?) #:value 4]{
Number of threads used by the minimum-conflicts solver.
Number of threads used by the @racket[min-conflicts-solver].
}
@defparam[current-node-consistency val (or/c #false procedure?) #:value #f]{
@ -419,6 +419,35 @@ Why does it help? Because lower-arity constraints tend to be faster to test, and
For instance, suppose we have variables representing positive integers @racket[a] and @racket[b] and the constraint says @racket[(< a b)]. Further suppose that @racket[b] is assigned value @racket[5]. At that point, this constraint can be ``rephrased'' as the one-arity function @racket[(< a 5)]. This implies that there are only four possible values for @racket[a] (namely, @racket['(1 2 3 4)])). If node consistency is active, the domain of @racket[a] can immediately be checked to see if it includes any of those values. But none of this is possible if we don't reduce the arity.
}
@section{Solvers}
Pass these functions to @racket[current-solver].
@defproc[(backtracking-solver
[prob csp?])
generator?]{
The default solver. Conducts an exhaustive, deterministic search of the state space. @italic{Backtracking} means that when the solver reaches a dead end in the search space, it unwinds to the last successful variable assignment and tries again. The details of its behavior are modified by @racket[current-select-variable], @racket[current-inference], and @racket[current-node-consistency].
The advantage of the backtracking solver: it proceeds through the search space in a systematic matter. If there is a solution, the backtracking solver will find it. Eventually.
The disadvantage: the same. Some search spaces are so huge, and the solutions so rare, that concentrating the effort on searching any particular branch is likely to be futile. For a more probabilistic approach, try @racket[min-conflicts-solver].
}
@defproc[(min-conflicts-solver
[prob csp?]
[max-steps exact-positive-integer? 100])
generator?]{
An alternative solver. Begins with a random assignment and then tries to minimize the number of conflicts (that is, constraint violations), up to @racket[_max-steps] (which defaults to 100). In essence, this is a probabilistic hill-climbing algorithm, where the solver makes random guesses and then tries to nudge those guesses toward the correct answer.
I like to imagine the solver flying above the search space with a planeload of paratroopers, who are dropped into the search territory. Each of them tries to walk from the place they land (= the initial random assignment) toward a solution.
It's a little weird that this works at all, but it does. Sometimes even better than the @racket[backtracking-solver], because the minimum-conflicts solver is ``sampling'' the search space at many diverse locations. Whereas the @racket[backtracking-solver] can get stuck in a fruitless area of the search space, the minimum-conflicts solver keeps moving around.
Of course, to avoid getting stuck, the minimum-conflicts solver has to abandon guesses that aren't panning out. Hence the @racket[_max-steps] argument, which controls the number of steps the solver takes on a certain attempt before giving up.
The other parameter that affects this solver is @racket[current-thread-count], which defaults to 4. The solver is multithreaded in the sense that it pursues multiple solutions simultaneously. This way, if one thread finds a solution earlier, it will not be blocked by the others.
}
@section{Selecting the next variable}

Loading…
Cancel
Save