11//! Branch-and-bound search.
22//!
3- //! BnB explores a binary tree where each [`Branch`] expands into two
4- //! children: an *inclusion* child that selects the candidate at the
5- //! branch's `cursor`, and an *exclusion* child that bans it along with
6- //! any same-(value, weight) duplicates that immediately follow.
3+ //! BnB explores a binary tree where each [`Branch`] expands into two children: an *inclusion*
4+ //! child that selects the candidate at the branch's `cursor`, and an *exclusion* child that bans
5+ //! it along with any same-(value, weight) duplicates that immediately follow.
76//!
8- //! Cursors only advance as we descend — inclusion's child has cursor
9- //! `parent + 1`; exclusion's jumps past the banned duplicates. That
10- //! invariant lets `insert_new_branches` skip directly to the next
11- //! undecided candidate without re-scanning `selected` / `banned`. The
12- //! only scanning happens to step past pre-selected / pre-banned positions
13- //! (rare, lazy).
14-
7+ //! Cursors only advance as we descend — inclusion's child has cursor `parent + 1`; exclusion's
8+ //! jumps past the banned duplicates. That invariant lets `insert_new_branches` skip directly to the
9+ //! next undecided candidate without re-scanning `selected` / `banned` from the start.
1510use core:: cmp:: Reverse ;
1611
1712use crate :: { float:: Ordf32 , Drain , SelectionCache , SelectionView , Target } ;
@@ -97,7 +92,9 @@ impl<'a, M: BnbMetric> BnbIter<'a, M> {
9792 is_exclusion : bool ,
9893 cursor : usize ,
9994 ) {
100- let bound = self . metric . bound ( & SelectionView :: with_cache ( cs, cache) , self . target ) ;
95+ let bound = self
96+ . metric
97+ . bound ( & SelectionView :: with_cache ( cs, cache) , self . target ) ;
10198 if let Some ( bound) = bound {
10299 let is_good_enough = match self . best {
103100 Some ( best) => best > bound,
@@ -115,16 +112,10 @@ impl<'a, M: BnbMetric> BnbIter<'a, M> {
115112 }
116113 }
117114
118- fn insert_new_branches (
119- & mut self ,
120- cs : & CoinSelector < ' a > ,
121- cache : & SelectionCache ,
122- start : usize ,
123- ) {
124- // Find the position to expand on: at or after `start`, whichever is
125- // the first candidate that's neither selected nor banned. Usually
126- // this *is* `start` — the only reason to advance is to skip past
127- // pre-selected/pre-banned candidates (see module-level docs).
115+ fn insert_new_branches ( & mut self , cs : & CoinSelector < ' a > , cache : & SelectionCache , start : usize ) {
116+ // Find the position to expand on: at or after `start`, whichever is the first candidate
117+ // that's neither selected nor banned. Usually this *is* `start` — the only reason to
118+ // advance is to skip past pre-selected/pre-banned candidates (see module-level docs).
128119 let mut iter = cs. candidates ( ) . skip ( start) ;
129120 let mut cursor = start;
130121 let ( here_idx, here_cand) = loop {
@@ -147,10 +138,9 @@ impl<'a, M: BnbMetric> BnbIter<'a, M> {
147138 inclusion_cache. add ( here_cand) ;
148139 self . consider_adding_to_queue ( & inclusion_cs, & inclusion_cache, false , cursor + 1 ) ;
149140
150- // Exclusion: descendants explore "this candidate is *not* selected".
151- // Bans this and every consecutive same-(value, weight) candidate —
152- // they're equivalent choices, so we deduplicate by handling the
153- // entire equivalence class in one branch. The cursor jumps past
141+ // Exclusion: descendants explore "this candidate is *not* selected". Bans this and every
142+ // consecutive same-(value, weight) candidate — they're equivalent choices, so we
143+ // deduplicate by handling the entire equivalence class in one branch. The cursor jumps past
154144 // all of them.
155145 let mut exclusion_cs = cs. clone ( ) ;
156146 exclusion_cs. ban ( here_idx) ;
@@ -173,9 +163,8 @@ struct Branch<'a> {
173163 selector : CoinSelector < ' a > ,
174164 cache : SelectionCache ,
175165 is_exclusion : bool ,
176- /// Position in `candidate_order` of the candidate whose include /
177- /// exclude decision creates this branch's two children. See the
178- /// module-level "Cursor invariant" section.
166+ /// Position in `candidate_order` of the candidate whose include/exclude decision creates this
167+ /// branch's two children. See the module-level docs for more.
179168 cursor : usize ,
180169}
181170
0 commit comments