Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
search_seq_os.hpp
Go to the documentation of this file.
1#ifndef FSTSEARCHSEQOS_H
2#define FSTSEARCHSEQOS_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 <vector>
54#include "error.hpp"
55#include "global.hpp"
56#include "stopwatch.hpp"
58#include "search_seq.hpp"
59
60/*============== Template parameter type naming conventions ==============
61--------- Numeric types: -------------------------------------------------
62DATATYPE - data sample values - usually real numbers (but may be integers
63 in text processing etc.)
64REALTYPE - must be real numbers - for representing intermediate results of
65 calculations like mean, covariance etc.
66IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
67 extent depends on numbers of samples in data
68DIMTYPE - index values for enumeration of features (dimensions), or classes (not
69 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
70 but be aware of expressions like _classes*_features*_features !
71 in linearized representations of feature matrices for all classes
72BINTYPE - feature selection marker type - represents ca. <10 different feature
73 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
74RETURNTYPE - criterion value: real value, but may be extended in future to support
75 multiple values
76--------- Class types: ---------------------------------------------------
77SUBSET - class of class type Subset
78CLASSIFIER - class implementing interface defined in abstract class Classifier
79EVALUATOR - class implementing interface defined in abstract class Sequential_Step
80DISTANCE - class implementing interface defined in abstract class Distance
81DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
82INTERVALCONTAINER - class of class type Data_Intervaller
83CONTAINER - STL container of class type Data_Interval
84========================================================================== */
85
86namespace FST {
87
100template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
101class Search_OS : public Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>{
102public:
104 typedef typename parent::parent grandparent;
106 typedef std::shared_ptr<SearchBIF> PSearchBIF;
107 typedef std::shared_ptr<EVALUATOR> PEvaluator;
108 typedef std::shared_ptr<CRITERION> PCriterion;
109 typedef std::shared_ptr<SUBSET> PSubset;
110 Search_OS(const PEvaluator evaluator) : Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>(evaluator), _delta(1), track(false) {parent::enable_initial_subset(); notify("Search_OS constructor.");}
111 virtual ~Search_OS() {notify("Search_OS destructor.");}
112
113 // NOTE: crit is expected to be already initialize()d before use here
114 virtual bool search(const DIMTYPE target_d, RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os=std::cout) override;
115
116 void set_delta(const DIMTYPE delta) {assert(delta>0); _delta=delta;}
117 DIMTYPE get_delta() const {return _delta;}
118
119 virtual std::ostream& print(std::ostream& os) const override;
120protected:
133 RESTART=0,
134 DOWN=1,
135 RETURN_UP=2,
136 UP=4,
137 RETURN_DOWN=8
138 };
139
141 virtual bool search_one(RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os);
152 virtual bool adjust_initial_subset(const DIMTYPE target_d, const PSubset sub, const PSearchBIF bif, std::ostream& os);
153
154 DIMTYPE _delta;
155
156 std::unique_ptr<SUBSET> pivotsub;
157 std::unique_ptr<SUBSET> pivot_top;
158 std::unique_ptr<SUBSET> pivot_bottom;
159
160 std::unique_ptr<SUBSET> inisub;
161 std::shared_ptr<SUBSET> tmpsub;
162private:
163 bool track;
164};
165
166template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
168{
169 if(parent::get_generalization_level()>1) os << "(G)";
170 os << "OS";
172 os << ", delta=" << _delta;
173 os << " [Search_OS()";
174 if(parent::_evaluator) os << " with " << *parent::_evaluator;
176 os << "]";
177 return os;
178}
179
180template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
181bool Search_OS<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>::adjust_initial_subset(const DIMTYPE target_d, const PSubset sub, const PSearchBIF bif, std::ostream& os)
182{
183 assert(sub);
184 assert(bif);
185 assert(target_d>0 && target_d<=sub->get_n());
186 RETURNTYPE value;
187 DIMTYPE f;
188
189 if(target_d!=0 && sub->get_d()!=target_d) {
190 if(grandparent::output_normal()) this->report(os, msgbuf() << "original initial "<< *sub << std::endl << std::flush);
191 // collect the BIF ranking (best -> worst) once; an undersized subset grows by
192 // selecting best-ranked features, an oversized one shrinks by deselecting
193 // WORST-ranked features - hence the reverse iteration in the shrink branch
194 std::vector<DIMTYPE> ranked;
195 if(!bif->getFirstFeature(value, f)) return false;
196 do {
197 assert(f<=sub->get_n());
198 ranked.push_back(f);
199 } while(bif->getNextFeature(value, f));
200 if(sub->get_d()<target_d) {
201 for(typename std::vector<DIMTYPE>::const_iterator it=ranked.begin();it!=ranked.end() && sub->get_d()!=target_d;++it) sub->select(*it);
202 } else {
203 for(typename std::vector<DIMTYPE>::const_reverse_iterator it=ranked.rbegin();it!=ranked.rend() && sub->get_d()!=target_d;++it) sub->deselect(*it);
204 }
205 if(sub->get_d()!=target_d) return false;
206 if(grandparent::output_normal()) this->report(os, msgbuf() << "adjusted initial "<< *sub << std::endl << std::endl << std::flush);
207 }
208 return true;
209}
210
211template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
212bool Search_OS<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
213{
214 // NOTE: certain parameter combinations (initial sub->get_d() versus _delta versus target_d versus parent::_generalization_level)
215 // make it impossible to yield a valid solution. In such case search() just returns false
216 StopWatch swatch;
217 if(parent::_evaluator) parent::_evaluator->set_output_detail(grandparent::get_output_detail()); else throw FST::fst_error("Search_Sequential: Missing evaluator.");
219 assert(sub);
220 assert(crit);
221 assert(sub->get_n()>0);
222 assert(target_d<sub->get_n());
224
225 if(!parent::initial_subset_enabled()) {sub->set_forward_mode(true); sub->deselect_all();} // deselect_all() writes mode-dependent ids: switch to forward mode FIRST, or a Subset left in backward mode by a previous search comes up in the wrong state
226
227 if( (_delta<parent::get_generalization_level()) ) return false; // impossible to run the search
228
230 msgbuf sos;
231 sos << "---------------------------------------" << std::endl;
232 sos << "Starting " << *this << std::endl;
233 sos << "with Criterion: " << *crit << std::endl;
234 sos << "with initial subset: " << *sub << std::endl;
235 if(target_d==0) sos << "Subset size to be optimized." << std::endl; else sos << "Target subset size set to: " << target_d << std::endl;
236 sos << std::endl << std::flush;
237 this->report(os,sos);
238 }
239
240 PSearchBIF bif;
241 DIMTYPE _n=sub->get_n();
242
243 if(target_d==0) { // optimize the cardinality: run one full oscillating search per subset size
244 // and keep the best result over all of them (at d==_n there is nothing to
245 // oscillate, so that one subset is merely evaluated)
246 if(!inisub || inisub->get_n()!=_n) inisub.reset(new SUBSET(_n));
247 if(!tmpsub || tmpsub->get_n()!=_n) tmpsub.reset(new SUBSET(_n));
248 RETURNTYPE value;
249 if(!bif) {
250 bif.reset(new SearchBIF());
251 if(!bif->evaluate_individuals(_n, crit, os)) return false;
252 }
253 inisub->stateless_copy(*sub);
254 for(DIMTYPE d=1;d<=_n;d++) {
255 tmpsub->stateless_copy(*inisub);
256 if(!adjust_initial_subset(d,tmpsub,bif,os)) return false;
257 if(d<_n) {if(!search_one(value,tmpsub,crit,os)) return false;} else {if(!crit->evaluate(value,tmpsub)) return false;}
258 if(d==1 || value>result) {
259 result=value;
260 sub->stateless_copy(*tmpsub);
261 if(grandparent::output_normal()) this->report_solution(os, result, *sub, msgbuf() << "NEW MAXVAL (d="<<d<<") ="<<result<<", " << *sub << std::endl << swatch << std::endl << std::endl << std::flush);
262 } else
263 if(grandparent::output_normal()) this->report_solution(os, value, *tmpsub, msgbuf() << "result (d="<<d<<") ="<<value<<", " << *tmpsub << std::endl << std::endl << std::flush);
264 }
265 } else { // evaluate subset of target_d features
266 if(sub->get_d()!=target_d) {
267 if(!bif) {
268 bif.reset(new SearchBIF);
269 if(!bif->evaluate_individuals(_n,crit,os)) return false;
270 }
271 if(!adjust_initial_subset(target_d,sub,bif,os)) return false;
272 }
273 if(!search_one(result,sub,crit,os)) return false;
274 }
276 msgbuf sos;
277 sos << "......................................." << std::endl;
278 sos << "Search_OS() search finished. " << swatch << std::endl;
279 sos << "Search result: "<< std::endl << *sub << std::endl << "Criterion value: " << result << std::endl << std::endl << std::flush;
280 this->report(os,sos);
281 }
282 return true;
283}
284
285template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
286bool Search_OS<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>::search_one(RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os) // returns found subset + criterion value
287{
288 notify("Search_OS::search_one().");
289 assert(sub);
290 assert(crit);
291 assert(sub->get_n()>0);
292 assert(sub->get_d()>0);
294
295 if( (sub->get_d()==0) ||
296 (_delta<parent::get_generalization_level()) ) return false; // impossible to run the search
297
298 DIMTYPE _n=sub->get_n();
299 if(!pivotsub || pivotsub->get_n()!=_n) pivotsub.reset(new SUBSET(_n));
300 if(!pivot_top || pivot_top->get_n()!=_n) pivot_top.reset(new SUBSET(_n));
301 if(!pivot_bottom || pivot_bottom->get_n()!=_n) pivot_bottom.reset(new SUBSET(_n));
302
303 RETURNTYPE bestval;
304
305 // sub is assumed to contain the initial subset
306 sub->set_forward_mode(true);
307
308 // The initial subset's value seeds the pivot, so this failure MUST be checked: continuing
309 // with an unset result would start the oscillation from an indeterminate pivot.
310 // NOTE this value comes from crit, while a Sequential_Step_Hybrid with keep_perc==0 selects
311 // by its filter criterion - which is why that combination is unsupported (see the class docs).
312 if(!crit->evaluate(result,sub)) return false; // the initial subset is the initially best pivot
313 if(track) grandparent::_tracker->add(result,sub);
314
315 SwingPhase swing=RESTART;
316
317 DIMTYPE _d=sub->get_d();
318 DIMTYPE _d_piv=_d;
319 DIMTYPE _cur_delta=parent::get_generalization_level();
320 bool pivot_top_avail=false;
321 bool pivot_bottom_avail=false;
322
323 bool swinged=false; // whether the current swing managed to take at least one step
324
325 // the oscillation itself: a state machine over SwingPhase (documented at its declaration),
326 // looping until the cycle depth _cur_delta has grown past the configured maximum _delta
327 while(_cur_delta<=_delta)
328 switch(swing) {
329 case RESTART:
330 assert(_d_piv==sub->get_d());
331 pivotsub->stateless_copy(*sub); bestval=result;
332 pivot_top_avail=pivot_bottom_avail=false;
334 swing=DOWN;
335 if(grandparent::output_normal()) this->report_solution(os, result, *sub, msgbuf() << "new subresult ="<<result<<", " << *sub << std::endl << std::endl << std::flush);
336 break;
337 case DOWN:
338 assert(_d==sub->get_d());
339 assert(sub->get_d()!=0);
340 if(pivot_bottom_avail) {assert(pivot_bottom->get_d()!=0); sub->stateless_copy(*pivot_bottom); _d=sub->get_d();}
341 swinged=false;
342 while(_d>parent::get_generalization_level() && _d_piv<_cur_delta+_d){
343 if(!parent::Step(false/*forward*/, result, sub, crit, os)) {if(grandparent::output_detailed()) this->report(os, msgbuf() << "DOWN failure" << std::endl << std::flush); return false;}
344 if(track) grandparent::_tracker->add(result,sub);
345 _d=sub->get_d(); swinged=true;
346 }
347 if(swinged) {
348 assert(_d==sub->get_d());
350 pivot_bottom_avail=true;
351 pivot_bottom->stateless_copy(*sub);
352 }
353 swing=RETURN_UP;
354 } else {
355 pivot_bottom_avail=false;
356 swing=UP;
357 }
358 assert(_d==sub->get_d());
359 break;
360 case RETURN_UP:
361 assert(_d==sub->get_d());
362 while(_d<_d_piv){
363 assert(_d==sub->get_d());
364 if(!parent::Step(true/*forward*/, result, sub, crit, os)) {if(grandparent::output_detailed()) this->report(os, msgbuf() << "RETURN UP failure" << std::endl << std::flush); return false;}
365 if(track) grandparent::_tracker->add(result,sub);
366 _d=sub->get_d();
367 }
368 if(result>bestval) swing=RESTART; // down-swing successful - reset
369 else {
370 sub->stateless_copy(*pivotsub);
371 swing=UP; swinged=false;
372 }
373 assert(_d==sub->get_d());
374 break;
375 case UP:
376 assert(_d==sub->get_d());
377 if(pivot_top_avail) {assert(pivot_top->get_d()!=0); sub->stateless_copy(*pivot_top); _d=sub->get_d();}
378 while(_d+parent::get_generalization_level()<=_n && _d<_cur_delta+_d_piv){
379 assert(_d==sub->get_d());
380 if(!parent::Step(true/*forward*/, result, sub, crit, os)) {if(grandparent::output_detailed()) this->report(os, msgbuf() << "UP failure" << std::endl << std::flush); return false;}
381 if(track) grandparent::_tracker->add(result,sub);
382 _d=sub->get_d(); swinged=true;
383 }
384 if(swinged) {
385 assert(_d==sub->get_d());
387 pivot_top_avail=true;
388 pivot_top->stateless_copy(*sub);
389 }
390 swing=RETURN_DOWN;
391 } else { // the UP swing could not move at all: deepen the cycle and try DOWN again
392 pivot_top_avail=false;
393 swing=DOWN;
395 }
396 assert(_d==sub->get_d());
397 break;
398 case RETURN_DOWN:
399 assert(_d==sub->get_d());
400 while(_d>_d_piv){
401 assert(_d==sub->get_d());
402 if(!parent::Step(false/*forward*/, result, sub, crit, os)) {if(grandparent::output_detailed()) this->report(os, msgbuf() << "RETURN DOWN failure" << std::endl << std::flush); return false;}
403 if(track) grandparent::_tracker->add(result,sub);
404 _d=sub->get_d();
405 }
406 if(result>bestval) swing=RESTART; // up-swing successful - reset
407 else { // neither swing improved at this depth: go back to the pivot and deepen the cycle
408 sub->stateless_copy(*pivotsub);
409 swing=DOWN; swinged=false;
411 }
412 assert(_d==sub->get_d());
413 break;
414 }
415
416 // the loop ends once the cycle depth exceeds _delta; the pivot is then the best subset found
417 if(pivotsub->get_d()!=0) {
418 sub->stateless_copy(*pivotsub);
419 result=bestval;
420 return true;
421 }
422 return false; // an empty pivot means no valid subset was ever adopted
423}
424
425
426} // namespace
427#endif // FSTSEARCHSEQOS_H ///:~
Implements Oscillating_Search.
Definition search_seq_os.hpp:101
std::shared_ptr< EVALUATOR > PEvaluator
shared pointer to the sequential-step evaluator
Definition search_seq_os.hpp:107
std::unique_ptr< SUBSET > pivot_bottom
the subset the last DOWN swing ended on, used the same way to resume a deeper DOWN swing without repe...
Definition search_seq_os.hpp:158
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_os.hpp:212
std::unique_ptr< SUBSET > pivot_top
the subset the last UP swing ended on; when the cycle depth grows, the next UP swing resumes from her...
Definition search_seq_os.hpp:157
std::unique_ptr< SUBSET > inisub
target_d==0 only: an untouched copy of the caller's initial subset, re-used as the starting point for...
Definition search_seq_os.hpp:160
std::unique_ptr< SUBSET > pivotsub
the current pivot: the best-known subset for the ongoing search_one() call, around which DOWN/UP swin...
Definition search_seq_os.hpp:156
SwingPhase
Oscillating-cycle phase for the OS swing state machine driving search_one()'s inline switch.
Definition search_seq_os.hpp:132
Search_Ranking_BIF< RETURNTYPE, DIMTYPE, SUBSET, CRITERION > SearchBIF
Best Individual Feature ranking search, used to grow/shrink an initial subset to the requested cardin...
Definition search_seq_os.hpp:105
void set_delta(const DIMTYPE delta)
oscillating cycle depth
Definition search_seq_os.hpp:116
virtual bool adjust_initial_subset(const DIMTYPE target_d, const PSubset sub, const PSearchBIF bif, std::ostream &os)
Brings sub to exactly target_d features before the oscillation starts: an undersized subset is supple...
Definition search_seq_os.hpp:181
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_os.hpp:167
std::shared_ptr< SUBSET > tmpsub
target_d==0 only: the working subset one cardinality is searched in, before it is compared with the b...
Definition search_seq_os.hpp:161
Search_OS(const PEvaluator evaluator)
evaluator must be non-null; unlike other sequential searches, initial_subset_enabled() is turned ON b...
Definition search_seq_os.hpp:110
std::shared_ptr< SUBSET > PSubset
shared pointer to a feature subset
Definition search_seq_os.hpp:109
Search_Sequential< RETURNTYPE, DIMTYPE, SUBSET, CRITERION, EVALUATOR > parent
base class alias
Definition search_seq_os.hpp:103
parent::parent grandparent
the shared Search base (output detail, result tracker)
Definition search_seq_os.hpp:104
bool track
whether result tracking is enabled, cached per search() call and read throughout search_one()
Definition search_seq_os.hpp:163
virtual bool search_one(RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream &os)
standard OS initialized by sub, target dimensionality sub->get_d()
Definition search_seq_os.hpp:286
std::shared_ptr< CRITERION > PCriterion
shared pointer to the criterion the search optimizes
Definition search_seq_os.hpp:108
std::shared_ptr< SearchBIF > PSearchBIF
shared pointer to a SearchBIF instance
Definition search_seq_os.hpp:106
DIMTYPE _delta
current oscillating cycle depth (max swing extent); see set_delta()
Definition search_seq_os.hpp:154
DIMTYPE get_delta() const
the currently configured oscillating cycle depth (see set_delta())
Definition search_seq_os.hpp:117
Implements Best Individual Features, i.e., individual feature ranking.
Definition search_ranking_bif.hpp:100
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
void enable_initial_subset()
makes search() start from the caller-supplied contents of its sub argument instead of the concrete me...
Definition search_seq.hpp:134
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
bool output_detailed() const
true only at the DETAILED output level
Definition search.hpp:160
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
Implements Best Individual Features, i.e., individual feature ranking.
Defines interface for sequential search method implementations.
Simple tool to measure run time.
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.