Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
search_seq_sfrs.hpp
Go to the documentation of this file.
1#ifndef FSTSEARCHSEQSFRS_H
2#define FSTSEARCHSEQSFRS_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>
52#include <sstream>
53#include "error.hpp"
54#include "global.hpp"
55#include "stopwatch.hpp"
56#include "search_seq.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
103template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
104class Search_SFRS : public Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>{
105public:
107 typedef typename parent::parent grandparent;
108 typedef std::shared_ptr<EVALUATOR> PEvaluator;
109 typedef std::shared_ptr<CRITERION> PCriterion;
110 typedef std::shared_ptr<SUBSET> PSubset;
111 Search_SFRS(const PEvaluator evaluator) : Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>(evaluator), _direction(FORWARD), _delta(0), _n(0) {notify("Search_SFRS constructor.");}
112 virtual ~Search_SFRS() {notify("Search_SFRS destructor.");}
113
114 // NOTE: crit is expected to be already initialize()d before use here
115 virtual bool search(const DIMTYPE target_d, RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os=std::cout) override;
116
117 void set_search_direction(const SearchDirection direction) {_direction=direction;}
119
120 void set_delta(const DIMTYPE delta) {_delta=delta;}
121 DIMTYPE get_delta() const {return _delta;}
122
123 bool get_result(const DIMTYPE d, RETURNTYPE &result, PSubset &sub);
124
125 virtual std::ostream& print(std::ostream& os) const override;
126protected:
129 struct OneSubset {
130 RETURNTYPE critvalue;
131 std::unique_ptr<SUBSET> sub;
132 };
133 DIMTYPE _delta;
134 std::unique_ptr<OneSubset[]> bsubs;
135 DIMTYPE _n;
136 std::unique_ptr<SUBSET> pivotsub;
137 std::unique_ptr<SUBSET> maxcritsub;
138};
139
140template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
142{
143 if(parent::get_generalization_level()>1) os << "(G)";
144 if(_direction==FORWARD) os << "SFRS"; else os << "SBRS";
146 os << ", delta=" << _delta;
147 os << " [Search_SFRS()";
148 if(parent::_evaluator) os << " with " << *parent::_evaluator;
150 os << "]";
151 return os;
152}
153
154template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
155bool Search_SFRS<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>::search(const DIMTYPE target_d, RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os) // returns found subset + criterion value
156{
157 // NOTE: so far this is the most original version of SFRS, i.e., it does
158 // not "correct misleading paths" (worse forward steps after successful backtracking) etc.
159 StopWatch swatch;
160 if(parent::_evaluator) parent::_evaluator->set_output_detail(grandparent::get_output_detail()); else throw FST::fst_error("Search_Sequential: Missing evaluator.");
162 assert(sub);
163 assert(crit);
164 assert(sub->get_n()>0);
165 assert(target_d<sub->get_n());
167
168 const bool _forward=(_direction==FORWARD);
169 sub->set_forward_mode(true); // select_all()/deselect_all() write mode-dependent ids - a Subset left in backward mode by a previous search() would otherwise re-initialize empty
171 if(_forward) sub->deselect_all(); else sub->select_all();
172 }
173 DIMTYPE _d=sub->get_d();
174 _n=sub->get_n();
175 bsubs.reset(new OneSubset[_n]); // reset BEFORE any early return, so get_result() never hands out a previous run's subsets
176 if( (!_forward && _d==0) ||
177 (target_d>0 && ((target_d>_d)?(target_d-_d):(_d-target_d))%parent::get_generalization_level()>0) ) return false; // no solution reachable under this setting
178
180 msgbuf sos;
181 sos << "---------------------------------------" << std::endl;
182 sos << "Starting " << *this << std::endl;
183 sos << "with Criterion: " << *crit << std::endl;
184 sos << "with initial subset: " << *sub << std::endl;
185 if(target_d==0) sos << "Subset size to be optimized." << std::endl; else sos << "Target subset size set to: " << target_d << std::endl;
186 sos << std::endl << std::flush;
187 this->report(os,sos);
188 }
189
190 if(!pivotsub || pivotsub->get_n()!=_n) pivotsub.reset(new SUBSET(_n));
191 if(!maxcritsub || maxcritsub->get_n()!=_n) maxcritsub.reset(new SUBSET(_n));
192 RETURNTYPE maxcritval;
193 bool havemax=false;
194 DIMTYPE _d_max;
195
196 sub->set_forward_mode(_forward); // the search's own direction; Step() flips it per step as needed
197
198 if(_d>0) { // evaluate initial subset if any
199 if(!crit->evaluate(result,sub)) return false;
200 if(track) grandparent::_tracker->add(result,sub);
201 bsubs[_d-1].sub.reset(new SUBSET(_n));
202 bsubs[_d-1].sub->stateless_copy(*sub);
203 bsubs[_d-1].critvalue=result;
204 maxcritsub->stateless_copy(*sub);
205 maxcritval=result;
206 _d_max = maxcritsub->get_d();
207 havemax=true;
208 if(grandparent::output_normal()) this->report_solution(os, maxcritval, *maxcritsub, msgbuf() << "initial MAXCRIT="<<maxcritval<<", " << *maxcritsub << std::endl << swatch << std::endl << std::endl << std::flush);
209 }
210
211 // Frontier bound in the search's OWN direction: SFRS keeps adding while _d+g <= _forward_thr
212 // (target_d+delta, or _n without a bound), SBRS keeps removing while _d >= _backward_thr+g
213 // (target_d-delta, or 1). Each threshold serves its own direction only; the retreat loop
214 // below always runs to the hard 1.._n bound, delta or not. A delta is meaningless when the
215 // cardinality itself is being optimized (target_d==0).
216 const DIMTYPE _forward_thr=(target_d>0 && _delta>0 && target_d+_delta<_n)?target_d+_delta:_n;
217 const DIMTYPE _backward_thr=(target_d>0 && _delta>0 && _delta<target_d)?target_d-_delta:1;
218 while( (_forward && _d+parent::get_generalization_level()<=_forward_thr) || (!_forward && _d>=_backward_thr+parent::get_generalization_level()) ) {
219 // unconditional step IN THE SEARCH'S OWN DIRECTION (adding a feature in SFRS, removing
220 // one in SBRS); the retreat below always steps the opposite way
221 if(!parent::Step(_forward, result, sub, crit, os)) return false;
222 if(track) grandparent::_tracker->add(result,sub);
223 _d=sub->get_d();
224
225 // tie rule: at EQUAL criterion value the smaller subset wins
226 if((!havemax) || (result>maxcritval) || (result==maxcritval && _d<_d_max)) {
227 maxcritsub->stateless_copy(*sub); maxcritval=result; _d_max=_d; havemax=true;
228 if(grandparent::output_normal()) this->report_solution(os, maxcritval, *maxcritsub, msgbuf() << "new MAXCRIT="<<maxcritval<<", " << *maxcritsub << std::endl << swatch << std::endl << std::endl << std::flush);
229 }
230 assert(_d-1<_n);
231 if(!bsubs[_d-1].sub) { // first result in that dimensionality
232 bsubs[_d-1].sub.reset(new SUBSET(_n));
233 bsubs[_d-1].sub->stateless_copy(*sub);
234 bsubs[_d-1].critvalue=result;
235 if(grandparent::output_normal()) this->report_solution(os, result, *sub, msgbuf() << "new subresult["<<_d<<"]="<<result<<", " << *sub << std::endl << std::endl << std::flush);
236 } else {
237 if(result > bsubs[_d-1].critvalue) { // better than before found for that dimensionality
238 bsubs[_d-1].sub->stateless_copy(*sub);
239 bsubs[_d-1].critvalue=result;
240 if(grandparent::output_normal()) this->report_solution(os, result, *sub, msgbuf() << "new subresult["<<_d<<"]="<<result<<", " << *sub << std::endl << std::endl << std::flush);
241 }
242 }
243
244 pivotsub->stateless_copy(*sub);
245 // THIS LOOP IS WHERE SFRS DIFFERS FROM SFFS. The retreat is unconditional: it keeps
246 // stepping back to the opposite cardinality bound whatever the values along the way,
247 // where SFFS would stop at the first step that does not improve its cardinality's
248 // record. Hence the larger number of evaluated subsets - and the second difference,
249 // the pivot rule below.
250 while((_forward && _d>=1+parent::get_generalization_level()) || (!_forward && _d+parent::get_generalization_level()<=_n)) {
251 if(!parent::Step(!_forward, result, sub, crit, os)) return false;
252 if(track) grandparent::_tracker->add(result,sub);
253 _d=sub->get_d();
254
255 // PIVOT RULE, the second SFRS-vs-SFFS difference: the subset the next advance resumes
256 // from is updated only when this retreat step produced a new OVERALL best (again with
257 // the smaller-subset tie rule), whereas SFFS pivots on any per-cardinality improvement
258 if((!havemax) || (result>maxcritval) || (result==maxcritval && _d<_d_max)) {
259 pivotsub->stateless_copy(*sub);
260 maxcritsub->stateless_copy(*sub); maxcritval=result; _d_max=_d; havemax=true;
261 if(grandparent::output_normal()) this->report_solution(os, maxcritval, *maxcritsub, msgbuf() << "new MAXCRIT="<<maxcritval<<", " << *maxcritsub << std::endl << swatch << std::endl << std::endl << std::flush);
262 }
263 assert(_d-1<_n);
264 if(!bsubs[_d-1].sub) { // first result in that dimensionality
265 bsubs[_d-1].sub.reset(new SUBSET(_n));
266 bsubs[_d-1].sub->stateless_copy(*sub);
267 bsubs[_d-1].critvalue=result;
268 if(grandparent::output_normal()) this->report_solution(os, result, *sub, msgbuf() << "new subresult["<<_d<<"]="<<result<<", " << *sub << std::endl << std::endl << std::flush);
269 } else {
270 if(result > bsubs[_d-1].critvalue) { // better than before found for that dimensionality
271 bsubs[_d-1].sub->stateless_copy(*sub);
272 bsubs[_d-1].critvalue=result;
273 if(grandparent::output_normal()) this->report_solution(os, result, *sub, msgbuf() << "new subresult["<<_d<<"]="<<result<<", " << *sub << std::endl << std::endl << std::flush);
274 }
275 }
276 }
277 sub->stateless_copy(*pivotsub);
278 _d=sub->get_d();
279 }
280 if(target_d>0) { // return subset of size target_d
281 assert(target_d>=1 && target_d<=_n);
282 if(!bsubs[target_d-1].sub) return false;
283 result=bsubs[target_d-1].critvalue;
284 sub->stateless_copy(*bsubs[target_d-1].sub);
285 } else { // find best dimensionality
286 if(!havemax) return false;
287 sub->stateless_copy(*maxcritsub);
288 result=maxcritval;
289 }
291 msgbuf sos;
292 sos << "......................................." << std::endl;
293 sos << "Search_SFRS() search finished. " << swatch << std::endl;
294 sos << "Search result: "<< std::endl << *sub << std::endl << "Criterion value: " << result << std::endl << std::endl << std::flush;
295 this->report(os,sos);
296 }
297 return true;
298}
299
300template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
301bool Search_SFRS<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>::get_result(const DIMTYPE d, RETURNTYPE &result, PSubset &sub) // retrieves temporary solution of cardinality d that had been updated in the course of the last search
302{
303 assert(sub);
304 assert(d>0 && d<=_n);
305 if(bsubs && d>0 && d<=_n && bsubs[d-1].sub) {
306 result=bsubs[d-1].critvalue;
307 sub->stateless_copy(*(bsubs[d-1].sub));
308 return true;
309 }
310 return false;
311}
312
313} // namespace
314#endif // FSTSEARCHSEQSFRS_H ///:~
Implements Sequential_Forward_Retreating_Search and Sequential_Backward_Retreating_Search.
Definition search_seq_sfrs.hpp:104
DIMTYPE get_delta() const
the currently configured delta bound (see set_delta())
Definition search_seq_sfrs.hpp:121
Search_SFRS(const PEvaluator evaluator)
evaluator must be non-null; direction defaults to FORWARD (SFRS); call set_search_direction(BACKWARD)...
Definition search_seq_sfrs.hpp:111
parent::parent grandparent
the shared Search base (output detail, result tracker)
Definition search_seq_sfrs.hpp:107
void set_delta(const DIMTYPE delta)
optional bound on how far the search's OWN direction advances the frontier: SFRS stops adding feature...
Definition search_seq_sfrs.hpp:120
bool get_result(const DIMTYPE d, RETURNTYPE &result, PSubset &sub)
retrieves temporary solution of cardinality d that had been updated in the course of the last search ...
Definition search_seq_sfrs.hpp:301
Search_Sequential< RETURNTYPE, DIMTYPE, SUBSET, CRITERION, EVALUATOR > parent
base class alias
Definition search_seq_sfrs.hpp:106
std::shared_ptr< CRITERION > PCriterion
shared pointer to the criterion the search optimizes
Definition search_seq_sfrs.hpp:109
SearchDirection get_search_direction() const
the currently configured direction (see set_search_direction())
Definition search_seq_sfrs.hpp:118
DIMTYPE _n
bsubs array size
Definition search_seq_sfrs.hpp:135
virtual bool search(const DIMTYPE target_d, RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream &os=std::cout) override
returns found subset + criterion value
Definition search_seq_sfrs.hpp:155
std::shared_ptr< EVALUATOR > PEvaluator
shared pointer to the sequential-step evaluator
Definition search_seq_sfrs.hpp:108
DIMTYPE _delta
frontier restriction around target_d (0 = unrestricted); see set_delta()
Definition search_seq_sfrs.hpp:133
std::unique_ptr< OneSubset[]> bsubs
per-cardinality best-known result found in the course of the last search() (index d-1); read by get_r...
Definition search_seq_sfrs.hpp:134
std::unique_ptr< SUBSET > maxcritsub
stores the subset with maximum known crit value
Definition search_seq_sfrs.hpp:137
std::shared_ptr< SUBSET > PSubset
shared pointer to a feature subset
Definition search_seq_sfrs.hpp:110
std::unique_ptr< SUBSET > pivotsub
the subset the next advance continues from: the last subset that became the overall best during the r...
Definition search_seq_sfrs.hpp:136
virtual std::ostream & print(std::ostream &os) const override
writes a human-readable identification of the search and its configuration; base default writes nothi...
Definition search_seq_sfrs.hpp:141
SearchDirection _direction
Definition search_seq_sfrs.hpp:127
void set_search_direction(const SearchDirection direction)
FORWARD selects SFRS, BACKWARD selects SBRS (do not change concurrently to running search()).
Definition search_seq_sfrs.hpp:117
Search< RETURNTYPE, DIMTYPE, SUBSET, CRITERION > parent
base class alias
Definition search_seq.hpp:107
Search_Sequential(const PEvaluator evaluator)
evaluator must be non-null
Definition search_seq.hpp:111
bool Step(const bool forward, RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream &os=std::cout)
Performs one sequential step: adds (forward) or removes (!forward) the best g-tuple of features,...
Definition search_seq.hpp:146
PEvaluator _evaluator
the sequential-step evaluator injected at construction; drives Step()'s candidate generation and best...
Definition search_seq.hpp:139
bool initial_subset_enabled() const
true if enable_initial_subset() was called (and not since disabled by disable_initial_subset())
Definition search_seq.hpp:136
DIMTYPE get_generalization_level() const
the currently configured generalization level (see set_generalization_level())
Definition search_seq.hpp:131
bool output_normal() const
true unless output detail is SILENT
Definition search.hpp:159
void report(std::ostream &os, const msgbuf &m) const
delivers one pre-formatted progress message: to the observer if attached, otherwise as syncout text t...
Definition search.hpp:170
bool result_tracker_active() const
true if a result tracker was assigned via enable_result_tracking() and not since disabled
Definition search.hpp:155
OutputDetail get_output_detail() const
the currently configured output detail level
Definition search.hpp:158
PResultTracker _tracker
the assigned result tracker, or null (see enable_result_tracking()/result_tracker_active())
Definition search.hpp:179
void report_solution(std::ostream &os, const RETURNTYPE value, const SUBSET &sub, const msgbuf &m) const
like report(), for sites announcing a new (sub-)solution; fires on_solution instead
Definition search.hpp:175
Simple stopwatch to measure run time of search algorithms.
Definition stopwatch.hpp:92
FST4 specific error passing class.
Definition error.hpp:61
Message accumulator for Search::report() - collects one progress message exactly as an std::ostringst...
Definition search.hpp:87
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
SearchDirection
Direction a sequential search proceeds in.
Definition search_seq.hpp:91
Defines interface for sequential search method implementations.
Simple tool to measure run time.
Structure to hold [subset,criterion value] temporary solutions in the course of search.
Definition search_seq_sfrs.hpp:129
RETURNTYPE critvalue
the subset's criterion value
Definition search_seq_sfrs.hpp:130
std::unique_ptr< SUBSET > sub
the subset itself, or null if no result was recorded at this cardinality
Definition search_seq_sfrs.hpp:131
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.