Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
search_branch_and_bound_fast.hpp
Go to the documentation of this file.
1#ifndef FSTSEARCHBRANCHANDBOUNDFAST_H
2#define FSTSEARCHBRANCHANDBOUNDFAST_H
3 /*
27=========================================================================
28Copyright and license:
29 * FST4 software (with the exception of any bundled or externally linked
30 third-party components) is Copyright (C) 2000-present Institute of
31 Information Theory and Automation of the Czech Academy of Sciences (UTIA).
32 * FST4 is free software: you can redistribute it and/or modify it under
33 the terms of the GNU Affero General Public License as published by the
34 Free Software Foundation, version 3. The full text of the license is in
35 the file LICENSE in the distribution root and at
36 https://www.gnu.org/licenses/agpl-3.0.html.
37 * FST4 is distributed in the hope that it will be useful, but WITHOUT ANY
38 WARRANTY; without even the implied warranty of MERCHANTABILITY or
39 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
40 License for more details.
41 * Closed-source use without the AGPL's copyleft obligations requires a
42 commercial license from UTIA; in case of interest write to
43 utia@utia.cas.cz. LICENSING.md in the distribution root explains both
44 options, the contributor license agreement that every contribution
45 requires, and the contacts (technical questions and pull requests:
46 somol@utia.cas.cz, with "FST" in the subject line).
47========================================================================== */
48
49#include <memory>
50#include <cassert>
51#include <iostream>
54#include "error.hpp"
55#include "global.hpp"
56#include "result_tracker.hpp"
57
58/*============== Template parameter type naming conventions ==============
59--------- Numeric types: -------------------------------------------------
60DATATYPE - data sample values - usually real numbers (but may be integers
61 in text processing etc.)
62REALTYPE - must be real numbers - for representing intermediate results of
63 calculations like mean, covariance etc.
64IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
65 extent depends on numbers of samples in data
66DIMTYPE - index values for enumeration of features (dimensions), or classes (not
67 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
68 but be aware of expressions like _classes*_features*_features !
69 in linearized representations of feature matrices for all classes
70BINTYPE - feature selection marker type - represents ca. <10 different feature
71 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
72RETURNTYPE - criterion value: real value, but may be extended in future to support
73 multiple values
74--------- Class types: ---------------------------------------------------
75SUBSET - class of class type Subset
76CLASSIFIER - class implementing interface defined in abstract class Classifier
77EVALUATOR - class implementing interface defined in abstract class Sequential_Step
78DISTANCE - class implementing interface defined in abstract class Distance
79DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
80INTERVALCONTAINER - class of class type Data_Intervaller
81CONTAINER - STL container of class type Data_Interval
82========================================================================== */
83
84namespace FST {
85
113
114template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class PREDICTOR>
115class Search_Branch_And_Bound_Fast : public Search_Branch_And_Bound<RETURNTYPE,DIMTYPE,SUBSET,CRITERION>{
116public:
119 typedef typename parent::PSubset PSubset;
120 typedef typename parent::PNode PNode;
121 typedef typename parent::Node Node;
122 typedef typename parent::NodeType NodeType;
124 explicit Search_Branch_And_Bound_Fast(PEvaluatorEngine engine=PEvaluatorEngine()):Search_Branch_And_Bound<RETURNTYPE,DIMTYPE,SUBSET,CRITERION>(engine) {notify("Search_Branch_And_Bound_Fast constructor.");}
125 virtual ~Search_Branch_And_Bound_Fast() {notify("Search_Branch_And_Bound_Fast destructor.");}
126
127 // for search() implementation see parent class
128
129 void set_gamma(const RETURNTYPE gamma=1.0) {_predictor.set_gamma(gamma);}
130 void set_delta(const unsigned int delta=1) {_predictor.set_delta(delta);}
131
133 virtual std::ostream& print(std::ostream& os) const override {
134 if(parent::get_engine()->is_threaded()) os << "Fast Branch & Bound (FBB) [Search_Branch_And_Bound_Fast(threads="<<parent::get_engine()->get_max_threads()<<") with " << _predictor << "]";
135 else os << "Fast Branch & Bound (FBB) [Search_Branch_And_Bound_Fast() with " << _predictor << "]";
136 return os;
137 };
138protected:
139 PREDICTOR _predictor;
140
141 // the following may be overriden in descendant Branch and Bound specialization classes
142 virtual void initialize(const DIMTYPE d, const DIMTYPE n, const PCriterion crit) override {_predictor.initialize(n);}
143 virtual void leaf_result(const DIMTYPE feature, const RETURNTYPE value) override;
144 virtual void pre_evaluate_availables() override;
145 virtual void post_process_tree_level() override {}
146 virtual bool cut_possible() override;
147};
148
149template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class PREDICTOR>
151{
152 // update the prediction info - but only from a parent whose value was actually
153 // computed: the drop learned here is parent value - leaf value, and against a
154 // merely PREDICTED parent value the predictor would be learning from its own
155 // guesses. The base class fires this after each leaf's bound update, in leaf order
156 Node &parentnode=parent::get_parent_node();
157 if(parentnode.type==parent::COMPUTED) _predictor.learn(feature,parentnode.value-value);
158}
159
160template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class PREDICTOR>
162{
163 assert(parent::get_criterion());
164 assert(parent::get_engine());
165 const PSubset &currentset=parent::get_currentset(); assert(currentset);
166 // no assert on the parent node's type here: FBB legitimately descends into
167 // PREDICTED nodes whenever cut_possible() declines to cut, so the parent node
168 // need not be COMPUTED
169 Node &parentnode=parent::get_parent_node();
170
171 // pass 1 (master thread): decide predict-vs-compute for every available feature.
172 // Deciding the whole level upfront gives the same decisions as deciding one
173 // feature at a time would, because predictor state is per-feature (the PREDICTOR
174 // contract): predict(f) reads only feature f's own statistics, each available is
175 // visited exactly once per level, and learn(f) fires only at f's own turn - so no
176 // decision can observe another feature's this-level learn
177 PNode avail;
178 RETURNTYPE val;
179 DIMTYPE to_compute=0;
180 for(bool got=this->getFirstAvailable(avail);got;got=this->getNextAvailable(avail))
181 {
182 if(_predictor.predict(avail->feature,val)) { //value could be predicted
183 avail->value=parentnode.value-val;
184 avail->type=parent::PREDICTED;
185 } else { //value could not be predicted
186 avail->type=parent::UNKNOWN; // computed by the engine batch below
187 ++to_compute;
188 }
189 }
190 if(to_compute==0) return;
191
192 // pass 2: batch-evaluate the non-predictable features through the engine; the
193 // per-result callback fires in add-order under both engine implementations, so
194 // values are assigned and prediction contrasts learned in one fixed order -
195 // master-thread decisions plus in-order result replay make a threaded run
196 // reproduce the sequential one exactly
197 typename parent::EVALUATORENGINE::ResultCallback on_result=[this,&parentnode](const RETURNTYPE value, const PSubset&, const DIMTYPE feat) {
198 PNode node;
199 if(this->getAvailable(feat,node)) {
200 node->value=value;
201 node->type=parent::COMPUTED;
202 // conditionally update prediction info
203 if(parentnode.type==parent::COMPUTED) _predictor.learn(feat,parentnode.value-value);
204 }
205 };
206 parent::get_engine()->initialize(parent::get_criterion(),parent::get_n(),on_result);
207 for(bool got=this->getFirstAvailable(avail);got;got=this->getNextAvailable(avail))
208 {
209 if(avail->type!=parent::PREDICTED) {
210 assert(currentset->selected_raw(avail->feature));
211 currentset->deselect_raw(avail->feature);
212 if(!parent::get_engine()->add_candidate(currentset,avail->feature)) throw FST::fst_error("Criterion evaluation failure.");
213 currentset->select_raw(avail->feature);
214 }
215 }
216 // a criterion exception thrown on a worker is rethrown here; a failed evaluation
217 // marks the batch failed with no callbacks fired - the nodes would stay UNKNOWN
218 // and poison the level sort-in, so the search must fail instead (same rationale
219 // as in the IBB body)
220 if(!parent::get_engine()->collect()) throw FST::fst_error("Criterion evaluation failure.");
221}
222
223template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class PREDICTOR>
225{ // NOTE: the lazy compute-before-cut below deliberately stays on the master thread -
226 // it is a single evaluation on the traversal's critical path (nothing to batch)
228 Node &currentnode=parent::get_current_node();
229 // in FBB the PREDICTED value is enough to evaluate cut as not possible
230 if(currentnode.value>parent::get_bound_value()) return false;
231 // but decision to cut must be always based on COMPUTED value...
232 if(currentnode.type!=parent::COMPUTED) {
233 const PSubset &currentset=parent::get_currentset(); assert(currentset);
234 assert(parent::get_criterion());
235 assert(currentnode.feature<parent::get_n());
236 assert(currentset->selected_raw(currentnode.feature));
237 currentset->deselect_raw(currentnode.feature);
238 if(!parent::get_criterion()->evaluate(currentnode.value,currentset)) throw FST::fst_error("Criterion evaluation failure.");
239 currentset->select_raw(currentnode.feature);
240 currentnode.type=parent::COMPUTED;
241 // conditionally update prediction info
242 // (no assert on the parent's type - the if below already handles the legitimate PREDICTED-parent case)
243 Node &parentnode=parent::get_parent_node();
244 if(parentnode.type==parent::COMPUTED) _predictor.learn(currentnode.feature,parentnode.value-currentnode.value);
245 }
246 if(currentnode.value<=parent::get_bound_value()) {
247 return true;
248 }
249 }
250 return false;
251}
252
253} // namespace
254#endif // FSTSEARCHBRANCHANDBOUNDFAST_H ///:~
Interface for Implementations of Prediction Mechanism in Fast Branch & Bound and Branch & Bound with ...
Implements Fast Branch and Bound, i.e., B&B with full utilization of prediction mechanism.
Definition search_branch_and_bound_fast.hpp:115
void set_delta(const unsigned int delta=1)
forwards to the PREDICTOR's set_delta() (in Branch_And_Bound_Predictor_Averaging the number of sample...
Definition search_branch_and_bound_fast.hpp:130
parent::NodeType NodeType
UNKNOWN/RANDOM/PREDICTED/COMPUTED node-value provenance, see Search_Branch_And_Bound.
Definition search_branch_and_bound_fast.hpp:122
virtual bool cut_possible() override
decides whether the sub-branch below the current node can be cut: a value above the bound rejects the...
Definition search_branch_and_bound_fast.hpp:224
virtual std::ostream & print(std::ostream &os) const override
writes the method's name, the predictor's own print() and, with a threaded engine,...
Definition search_branch_and_bound_fast.hpp:133
virtual void pre_evaluate_availables() override
orders the available features for the next tree level: each feature's value is predicted where the pr...
Definition search_branch_and_bound_fast.hpp:161
parent::PNode PNode
pointer to a search-tree Node, see Search_Branch_And_Bound
Definition search_branch_and_bound_fast.hpp:120
parent::Node Node
search-tree node record (feature, value, type, valid), see Search_Branch_And_Bound
Definition search_branch_and_bound_fast.hpp:121
PREDICTOR _predictor
the prediction mechanism instance (PREDICTOR type parameter, e.g. Branch_And_Bound_Predictor_Averagin...
Definition search_branch_and_bound_fast.hpp:139
parent::PCriterion PCriterion
shared pointer to the criterion the search optimizes
Definition search_branch_and_bound_fast.hpp:118
parent::PEvaluatorEngine PEvaluatorEngine
shared pointer to the injected evaluation-strategy engine
Definition search_branch_and_bound_fast.hpp:123
parent::PSubset PSubset
shared pointer to a feature subset
Definition search_branch_and_bound_fast.hpp:119
void set_gamma(const RETURNTYPE gamma=1.0)
forwards to the PREDICTOR's set_gamma() (in Branch_And_Bound_Predictor_Averaging the scaling factor a...
Definition search_branch_and_bound_fast.hpp:129
virtual void initialize(const DIMTYPE d, const DIMTYPE n, const PCriterion crit) override
resets the predictor's per-feature statistics before each search - nothing is carried over from a pre...
Definition search_branch_and_bound_fast.hpp:142
Search_Branch_And_Bound_Fast(PEvaluatorEngine engine=PEvaluatorEngine())
engine forwarded to Search_Branch_And_Bound; defaults to sequential in-line evaluation if not supplie...
Definition search_branch_and_bound_fast.hpp:124
virtual void post_process_tree_level() override
nothing to do: a PREDICTED node value is what makes FBB fast, and the values are turned into computed...
Definition search_branch_and_bound_fast.hpp:145
Search_Branch_And_Bound< RETURNTYPE, DIMTYPE, SUBSET, CRITERION > parent
base class alias
Definition search_branch_and_bound_fast.hpp:117
virtual void leaf_result(const DIMTYPE feature, const RETURNTYPE value) override
feeds one more observed criterion-value drop to the predictor; fired per leaf, in candidate order,...
Definition search_branch_and_bound_fast.hpp:150
PSubset & get_currentset()
mutable accessor for _currentset, for use by descendant B&B variants' hooks
Definition search_branch_and_bound.hpp:198
Node & get_parent_node()
the node one level up (_tree[k-1].back()) whose feature removal defines the branch currently being fo...
Definition search_branch_and_bound.hpp:199
Node * PNode
pointer to a Node, as used by the getFirst.../getNext... accessors below
Definition search_branch_and_bound.hpp:142
PCriterion const & get_criterion() const
accessor for _criterion, for use by descendant B&B variants' hooks
Definition search_branch_and_bound.hpp:180
Search_Branch_And_Bound(PEvaluatorEngine engine=PEvaluatorEngine())
Constructs the search, optionally injecting a candidate-evaluation engine.
Definition search_branch_and_bound.hpp:121
DIMTYPE n
no. of all features
Definition search_branch_and_bound.hpp:176
Node & get_current_node()
the node at the current level k presently being visited (_tree[k].back()), i.e. about to be tested fo...
Definition search_branch_and_bound.hpp:200
PEvaluatorEngine const & get_engine() const
accessor for the injected evaluation strategy (sequential-inline or thread-pooled),...
Definition search_branch_and_bound.hpp:181
bool getAvailable(DIMTYPE idx, PNode &avail)
direct lookup of _available[idx] by feature index, regardless of its valid flag; returns false if idx...
Definition search_branch_and_bound.hpp:195
bool getFirstAvailable(PNode &avail)
begins iteration over _available in feature-index order, skipping withdrawn (invalid) entries,...
Definition search_branch_and_bound.hpp:193
DIMTYPE d
no. of features to be selected
Definition search_branch_and_bound.hpp:177
std::shared_ptr< EVALUATORENGINE > PEvaluatorEngine
shared pointer to an EVALUATORENGINE, as accepted by the constructor and returned by get_engine()
Definition search_branch_and_bound.hpp:115
bool is_bound_valid() const
true once a valid bound has been established, see _boundvalid
Definition search_branch_and_bound.hpp:184
std::shared_ptr< SUBSET > PSubset
shared pointer to a feature subset
Definition search_branch_and_bound.hpp:113
bool getNextAvailable(PNode &avail)
advances the getFirstAvailable() iteration, again skipping invalid entries; returns false once exhaus...
Definition search_branch_and_bound.hpp:194
std::shared_ptr< CRITERION > PCriterion
shared pointer to the criterion under optimization
Definition search_branch_and_bound.hpp:112
NodeType
Marks how a Node's value was obtained: UNKNOWN = not yet assigned; RANDOM = reserved but currently un...
Definition search_branch_and_bound.hpp:134
DIMTYPE get_n() const
total number of features, see n
Definition search_branch_and_bound.hpp:187
RETURNTYPE get_bound_value() const
the current best (bound) criterion value; meaningful only once is_bound_valid()
Definition search_branch_and_bound.hpp:183
FST4 specific error passing class.
Definition error.hpp:61
FST4 specific error passing class.
Global definitions.
The Feature Selection Toolbox 4 library: every library component – data access, criteria and classifi...
Definition data_accessor.hpp:80
void notify(const std::string msg)
Prints msg followed by a newline via syncout::print(); a no-op unless the preprocessor macro DEBUG is...
Definition global.hpp:112
Defines interface for classes that enable collecting multiple results.
Implements Branch and Bound template method as basis for more advanced B&B implementations.
Structure representing search tree node.
Definition search_branch_and_bound.hpp:136
RETURNTYPE value
the node's criterion value: exact once type==COMPUTED, an ordering estimate while type==PREDICTED,...
Definition search_branch_and_bound.hpp:138
DIMTYPE feature
the feature index this node represents removing from its ancestor set (meaningless in the single root...
Definition search_branch_and_bound.hpp:137
NodeType type
how value was obtained, see NodeType
Definition search_branch_and_bound.hpp:139
Copyright © 2000–present ÚTIA, Institute of Information Theory and Automation of the Czech Academy of Sciences · Contacts · This site sets no cookies and uses no analytics.