Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
search_seq_sffs.hpp
Go to the documentation of this file.
1#ifndef FSTSEARCHSEQSFFS_H
2#define FSTSEARCHSEQSFFS_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
98template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
99class Search_SFFS : public Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>{
100public:
102 typedef typename parent::parent grandparent;
103 typedef std::shared_ptr<EVALUATOR> PEvaluator;
104 typedef std::shared_ptr<CRITERION> PCriterion;
105 typedef std::shared_ptr<SUBSET> PSubset;
106 Search_SFFS(const PEvaluator evaluator) : Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>(evaluator), _direction(FORWARD), _delta(0), _n(0) {notify("Search_SFFS constructor.");}
107 virtual ~Search_SFFS() {notify("Search_SFFS destructor.");}
108
109 // NOTE: crit is expected to be already initialize()d before use here
110 virtual bool search(const DIMTYPE target_d, RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os=std::cout) override;
111
112 void set_search_direction(const SearchDirection direction) {_direction=direction;}
114
115 void set_delta(const DIMTYPE delta) {_delta=delta;}
116 DIMTYPE get_delta() const {return _delta;}
117
118 bool get_result(const DIMTYPE d, RETURNTYPE &result, PSubset &sub);
119
120 virtual std::ostream& print(std::ostream& os) const override;
121protected:
124 struct OneSubset {
125 RETURNTYPE critvalue;
126 std::unique_ptr<SUBSET> sub;
127 };
128 DIMTYPE _delta;
129 std::unique_ptr<OneSubset[]> bsubs;
130 DIMTYPE _n;
131 std::unique_ptr<SUBSET> pivotsub;
132 std::unique_ptr<SUBSET> maxcritsub;
133};
134
135template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
137{
138 if(parent::get_generalization_level()>1) os << "(G)";
139 if(_direction==FORWARD) os << "SFFS"; else os << "SBFS";
141 os << ", delta=" << _delta;
142 os << " [Search_SFFS()";
143 if(parent::_evaluator) os << " with " << *parent::_evaluator;
145 os << "]";
146 return os;
147}
148
149template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
150bool Search_SFFS<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
151{
152 // NOTE: certain parameter combinations (initial sub->get_d() versus target_d versus parent::_generalization_level)
153 // make it impossible to yield a valid solution. In such case search() just returns false
154 // NOTE: so far this is the most original version of SFFS, i.e., it does
155 // not "correct misleading paths" ("worse-than-before" forward steps after successful backtracking) etc.
156 StopWatch swatch;
157 if(parent::_evaluator) parent::_evaluator->set_output_detail(grandparent::get_output_detail()); else throw FST::fst_error("Search_Sequential: Missing evaluator.");
159 assert(sub);
160 assert(crit);
161 assert(sub->get_n()>0);
162 assert(target_d<sub->get_n());
164
165 const bool _forward=(_direction==FORWARD);
166 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
168 if(_forward) sub->deselect_all(); else sub->select_all();
169 }
170 DIMTYPE _d=sub->get_d();
171 if( (!_forward && _d==0) ||
172 (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
173
175 msgbuf sos;
176 sos << "---------------------------------------" << std::endl;
177 sos << "Starting " << *this << std::endl;
178 sos << "with Criterion: " << *crit << std::endl;
179 sos << "with initial subset: " << *sub << std::endl;
180 if(target_d==0) sos << "Subset size to be optimized." << std::endl; else sos << "Target subset size set to: " << target_d << std::endl;
181 sos << std::endl << std::flush;
182 this->report(os,sos);
183 }
184
185 _n=sub->get_n();
186 bsubs.reset(new OneSubset[_n]);
187 if(!pivotsub || pivotsub->get_n()!=_n) pivotsub.reset(new SUBSET(_n));
188 if(!maxcritsub || maxcritsub->get_n()!=_n) maxcritsub.reset(new SUBSET(_n));
189 RETURNTYPE maxcritval;
190 bool havemax=false;
191 DIMTYPE _d_max;
192
193 sub->set_forward_mode(_forward); // the search's own direction; Step() flips it per step as needed
194 if(_d>0) { // evaluate initial subset if any
195 if(!crit->evaluate(result,sub)) return false;
196 if(track) grandparent::_tracker->add(result,sub);
197 bsubs[_d-1].sub.reset(new SUBSET(_n));
198 bsubs[_d-1].sub->stateless_copy(*sub);
199 bsubs[_d-1].critvalue=result;
200 maxcritsub->stateless_copy(*sub);
201 maxcritval=result;
202 _d_max = maxcritsub->get_d();
203 havemax=true;
204 if(grandparent::output_normal()) this->report_solution(os, maxcritval, *maxcritsub, msgbuf() << "initial MAXCRIT="<<maxcritval<<", " << *maxcritsub << std::endl << swatch << std::endl << std::endl << std::flush);
205 }
206
207 // Cardinality window the floating frontier may move in. Without a delta bound it is the
208 // full 1.._n range; with one it narrows to target_d-delta .. target_d+delta, clipped to
209 // that full range. A delta is meaningless when the cardinality itself is being optimized
210 // (target_d==0), so it is ignored then.
211 const DIMTYPE _forward_thr=(target_d>0 && _delta>0 && target_d+_delta<_n)?target_d+_delta:_n;
212 const DIMTYPE _backward_thr=(target_d>0 && _delta>0 && _delta<target_d)?target_d-_delta:1;
213 while( (_forward && _d+parent::get_generalization_level()<=_forward_thr) || (!_forward && _d>=_backward_thr+parent::get_generalization_level()) ) {
214 // unconditional step IN THE SEARCH'S OWN DIRECTION (adding a feature in SFFS, removing
215 // one in SBFS); the conditional backtracking below always steps the opposite way
216 if(!parent::Step(_forward, result, sub, crit, os)) return false;
217 if(track) grandparent::_tracker->add(result,sub);
218 _d=sub->get_d();
219
220 // tie rule: at EQUAL criterion value the smaller subset wins
221 if((!havemax) || (result>maxcritval) || (result==maxcritval && _d<_d_max)) {
222 maxcritsub->stateless_copy(*sub); maxcritval=result; _d_max=_d; havemax=true;
223 if(grandparent::output_normal()) this->report_solution(os, maxcritval, *maxcritsub, msgbuf() << "new MAXCRIT="<<maxcritval<<", " << *maxcritsub << std::endl << swatch << std::endl << std::endl << std::flush);
224 }
225 assert(_d-1<_n);
226 if(!bsubs[_d-1].sub) { // first result in that dimensionality
227 bsubs[_d-1].sub.reset(new SUBSET(_n));
228 bsubs[_d-1].sub->stateless_copy(*sub);
229 bsubs[_d-1].critvalue=result;
230 if(grandparent::output_normal()) this->report_solution(os, result, *sub, msgbuf() << "new subresult["<<_d<<"]="<<result<<", " << *sub << std::endl << std::endl << std::flush);
231 } else {
232 if(result > bsubs[_d-1].critvalue) { // better than before found for that dimensionality
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 }
237 }
238
239 pivotsub->stateless_copy(*sub);
240 // Conditional BACKTRACKING: keep stepping back as long as each step still IMPROVES the
241 // record of the cardinality it lands on, remembering the last improving subset in
242 // pivotsub. The loop ends on the first step that does not improve (backtrack=false) or
243 // when the cardinality bound is reached; the outer loop then resumes from pivotsub, so a
244 // non-improving excursion is discarded. A record is replaced only on a strictly better
245 // value, hence an equally good subset does not prolong the backtracking either.
246 bool backtrack=true;
247 while(backtrack && ((_forward && _d>=1+parent::get_generalization_level()) || (!_forward && _d+parent::get_generalization_level()<=_n))) {
248 if(!parent::Step(!_forward, result, sub, crit, os)) return false;
249 if(track) grandparent::_tracker->add(result,sub);
250 _d=sub->get_d();
251
252 if((!havemax) || (result>maxcritval) || (result==maxcritval && _d<_d_max)) {
253 maxcritsub->stateless_copy(*sub); maxcritval=result; _d_max=_d; havemax=true;
254 if(grandparent::output_normal()) this->report_solution(os, maxcritval, *maxcritsub, msgbuf() << "new MAXCRIT="<<maxcritval<<", " << *maxcritsub << std::endl << swatch << std::endl << std::endl << std::flush);
255 }
256 assert(_d-1<_n);
257 if(!bsubs[_d-1].sub) { // first result in that dimensionality
258 bsubs[_d-1].sub.reset(new SUBSET(_n));
259 bsubs[_d-1].sub->stateless_copy(*sub);
260 bsubs[_d-1].critvalue=result;
261 if(grandparent::output_normal()) this->report_solution(os, result, *sub, msgbuf() << "new subresult["<<_d<<"]="<<result<<", " << *sub << std::endl << std::endl << std::flush);
262 } else {
263 if(result > bsubs[_d-1].critvalue) { // better than before found for that dimensionality
264 bsubs[_d-1].sub->stateless_copy(*sub);
265 bsubs[_d-1].critvalue=result;
266 pivotsub->stateless_copy(*sub);
267 if(grandparent::output_normal()) this->report_solution(os, result, *sub, msgbuf() << "new subresult["<<_d<<"]="<<result<<", " << *sub << std::endl << std::endl << std::flush);
268 } else backtrack=false;
269 }
270 }
271 sub->stateless_copy(*pivotsub);
272 _d=sub->get_d();
273 }
274 if(target_d>0) { // return subset of size target_d
275 assert(target_d>=1 && target_d<=_n);
276 if(!bsubs[target_d-1].sub) return false;
277 result=bsubs[target_d-1].critvalue;
278 sub->stateless_copy(*bsubs[target_d-1].sub);
279 } else { // find best dimensionality
280 if(!havemax) return false;
281 sub->stateless_copy(*maxcritsub);
282 result=maxcritval;
283 }
285 msgbuf sos;
286 sos << "......................................." << std::endl;
287 sos << "Search_SFFS() search finished. " << swatch << std::endl;
288 sos << "Search result: "<< std::endl << *sub << std::endl << "Criterion value: " << result << std::endl << std::endl << std::flush;
289 this->report(os,sos);
290 }
291 return true;
292}
293
294template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
295bool Search_SFFS<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
296{
297 assert(sub);
298 assert(d>0 && d<=_n);
299 if(bsubs && d>0 && d<=_n && bsubs[d-1].sub) {
300 result=bsubs[d-1].critvalue;
301 sub->stateless_copy(*(bsubs[d-1].sub));
302 return true;
303 }
304 return false;
305}
306
307} // namespace
308#endif // FSTSEARCHSEQSFFS_H ///:~
Implements Sequential_Forward_Floating_Search and Sequential_Backward_Floating_Search.
Definition search_seq_sffs.hpp:99
std::shared_ptr< CRITERION > PCriterion
shared pointer to the criterion the search optimizes
Definition search_seq_sffs.hpp:104
DIMTYPE get_delta() const
the currently configured delta bound (see set_delta())
Definition search_seq_sffs.hpp:116
parent::parent grandparent
the shared Search base (output detail, result tracker)
Definition search_seq_sffs.hpp:102
std::shared_ptr< SUBSET > PSubset
shared pointer to a feature subset
Definition search_seq_sffs.hpp:105
std::unique_ptr< SUBSET > pivotsub
the subset the next forward step continues from: the deepest backtracking result that improved its ca...
Definition search_seq_sffs.hpp:131
std::unique_ptr< SUBSET > maxcritsub
stores the subset with maximum known crit value
Definition search_seq_sffs.hpp:132
DIMTYPE _n
bsubs array size
Definition search_seq_sffs.hpp:130
void set_search_direction(const SearchDirection direction)
FORWARD selects SFFS, BACKWARD selects SBFS (do not change concurrently to a running search()).
Definition search_seq_sffs.hpp:112
void set_delta(const DIMTYPE delta)
optional parameter to restrict search extent: bounds the floating frontier to at most target_d+delta ...
Definition search_seq_sffs.hpp:115
Search_Sequential< RETURNTYPE, DIMTYPE, SUBSET, CRITERION, EVALUATOR > parent
base class alias
Definition search_seq_sffs.hpp:101
SearchDirection _direction
Definition search_seq_sffs.hpp:122
SearchDirection get_search_direction() const
the currently configured direction (see set_search_direction())
Definition search_seq_sffs.hpp:113
Search_SFFS(const PEvaluator evaluator)
evaluator must be non-null; direction defaults to FORWARD (SFFS); call set_search_direction(BACKWARD)...
Definition search_seq_sffs.hpp:106
std::shared_ptr< EVALUATOR > PEvaluator
shared pointer to the sequential-step evaluator
Definition search_seq_sffs.hpp:103
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_sffs.hpp:295
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_sffs.hpp:150
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_sffs.hpp:129
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_sffs.hpp:136
DIMTYPE _delta
frontier restriction around target_d (0 = unrestricted); see set_delta()
Definition search_seq_sffs.hpp:128
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_sffs.hpp:124
RETURNTYPE critvalue
the subset's criterion value
Definition search_seq_sffs.hpp:125
std::unique_ptr< SUBSET > sub
the subset itself, or null if no result was recorded at this cardinality
Definition search_seq_sffs.hpp:126
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.