Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
search_seq_dos.hpp
Go to the documentation of this file.
1#ifndef FSTSEARCHSEQDOS_H
2#define FSTSEARCHSEQDOS_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
96template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
97class Search_DOS : public Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>{
98public:
100 typedef typename parent::parent grandparent;
101 typedef std::shared_ptr<EVALUATOR> PEvaluator;
102 typedef std::shared_ptr<CRITERION> PCriterion;
103 typedef std::shared_ptr<SUBSET> PSubset;
104 Search_DOS(const PEvaluator evaluator) : Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>(evaluator), _delta(3) {parent::enable_initial_subset(); notify("Search_DOS constructor.");}
105 virtual ~Search_DOS() {notify("Search_DOS destructor.");}
106
107 // NOTE: crit is expected to be already initialize()d before use here
108 // NOTE: target_d is ignored
109 virtual bool search(const DIMTYPE target_d, RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os=std::cout) override;
110
111 void set_delta(const DIMTYPE delta) {assert(delta>0); _delta=delta;}
112 DIMTYPE get_delta() const {return _delta;}
113
114 virtual std::ostream& print(std::ostream& os) const override;
115protected:
130 RESTART=0,
131 DOWN=1,
132 RETURN_UP=2,
133 UP=4,
134 RETURN_DOWN=8
135 };
136 DIMTYPE _delta;
137
138 std::unique_ptr<SUBSET> pivotsub;
139 std::unique_ptr<SUBSET> pivot_top;
140 std::unique_ptr<SUBSET> pivot_bottom;
141};
142
143template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
145{
146 if(parent::get_generalization_level()>1) os << "(G)";
147 os << "DOS";
149 os << ", delta=" << _delta;
150 os << " [Search_DOS()";
151 if(parent::_evaluator) os << " with " << *parent::_evaluator;
153 os << "]";
154 return os;
155}
156
157template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
158bool Search_DOS<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
159{
160 StopWatch swatch;
161 if(parent::_evaluator) parent::_evaluator->set_output_detail(grandparent::get_output_detail()); else throw FST::fst_error("Search_Sequential: Missing evaluator.");
163 assert(sub);
164 assert(crit);
165 assert(sub->get_n()>0);
167
168 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
169
170 if( (_delta<parent::get_generalization_level()) ) return false; // impossible to run the search
171
173 msgbuf sos;
174 sos << "---------------------------------------" << std::endl;
175 sos << "Starting " << *this << std::endl;
176 sos << "with Criterion: " << *crit << std::endl;
177 sos << "with initial subset: " << *sub << std::endl;
178 sos << "Subset size to be optimized." << std::endl << std::endl << std::flush;
179 this->report(os,sos);
180 }
181
182 DIMTYPE _n=sub->get_n();
183 if(!pivotsub || pivotsub->get_n()!=_n) pivotsub.reset(new SUBSET(_n));
184 if(!pivot_top || pivot_top->get_n()!=_n) pivot_top.reset(new SUBSET(_n));
185 if(!pivot_bottom || pivot_bottom->get_n()!=_n) pivot_bottom.reset(new SUBSET(_n));
186
187 RETURNTYPE pivotval; // pivotsub crit value (~max known crit value)
188
189 // sub is assumed to contain the initial subset, or to be empty to request standard DOS course of search
190 // NOTE: initializing DOS with Sequential_Step_Hybrid() and _keep_perc==0 does not allow proper initialization of 'result'.
191
192 sub->set_forward_mode(true);
193 if(sub->get_d()==0) {
194 if(!parent::Step(true/*forward*/, result, sub, crit, os)) return false; // find first subset
195 } else {
196 // the initial subset's value seeds the pivot, so this failure MUST be checked: continuing
197 // with an unset result would start the oscillation from an indeterminate pivot. NOTE this
198 // value comes from crit, while a Sequential_Step_Hybrid with keep_perc==0 selects by its
199 // filter criterion - which is why a non-empty init is unsupported there (see the class docs)
200 if(!crit->evaluate(result,sub)) return false;
201 }
202 if(track) grandparent::_tracker->add(result,sub);
203
204 SwingPhase swing=RESTART;
205
206 DIMTYPE _d=sub->get_d();
207 DIMTYPE _d_piv;
208 DIMTYPE _cur_delta=parent::get_generalization_level();
209 bool pivot_top_avail=false;
210 bool pivot_bottom_avail=false;
211
212 bool swinged=false; // whether the current swing managed to take at least one step
213
214 // The oscillation itself: a state machine over SwingPhase (documented at its declaration),
215 // looping until the cycle depth _cur_delta has grown past the configured maximum _delta.
216 // `goto label` below re-enters the switch immediately, without re-testing that loop
217 // condition; it is only ever used to jump to RESTART, which resets _cur_delta to the
218 // minimum anyway, so the condition cannot be violated by the shortcut. On re-entry _d must
219 // equal sub->get_d() and `result` must be the value of that very subset - both hold at every
220 // jump site, which is right after a successful Step() on `sub`.
221 while(_cur_delta<=_delta) {
222 label: switch(swing) {
223 case RESTART:
224 assert(_d==sub->get_d());
225 _d_piv=_d;
226 pivotsub->stateless_copy(*sub); pivotval=result;
227 pivot_top_avail=pivot_bottom_avail=false;
228 assert(pivotsub->get_d()==_d);
229 if(grandparent::output_normal()) this->report_solution(os, pivotval, *pivotsub, msgbuf() << "new pivmax="<<pivotval<<", " << *pivotsub << std::endl << swatch << std::endl << std::endl << std::flush);
231 swing=DOWN;
232 assert(_d==sub->get_d());
233 break;
234 case DOWN:
235 assert(_d==sub->get_d());
236 assert(_d <= _d_piv);
237 assert(sub->get_d()!=0);
238 if(pivot_bottom_avail) {assert(pivot_bottom->get_d()!=0); sub->stateless_copy(*pivot_bottom); _d=sub->get_d();}
239 swinged=false;
240 while(_d>parent::get_generalization_level() && _d_piv<_cur_delta+_d){
241 assert(_d==sub->get_d());
242 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;}
243 if(track) grandparent::_tracker->add(result,sub);
244 _d=sub->get_d(); swinged=true;
245 if(result>pivotval || (result==pivotval && _d<_d_piv)) {swing=RESTART; goto label;} // the DOS to OS difference: restart as soon as the pivot is beaten, equal value with fewer features included
246 }
247 if(swinged) {
248 assert(_d==sub->get_d());
250 pivot_bottom_avail=true;
251 pivot_bottom->stateless_copy(*sub);
252 }
253 swing=RETURN_UP;
254 } else {
255 pivot_bottom_avail=false;
256 swing=UP;
257 }
258 assert(_d==sub->get_d());
259 break;
260 case RETURN_UP:
261 assert(_d==sub->get_d());
262 assert(_d<_d_piv);
263 while(_d<_d_piv){
264 assert(_d==sub->get_d());
265 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;}
266 if(track) grandparent::_tracker->add(result,sub);
267 _d=sub->get_d();
268 if(result>pivotval || (result==pivotval && _d<_d_piv)) {swing=RESTART; goto label;} // the DOS to OS difference: restart as soon as the pivot is beaten, equal value with fewer features included
269 }
270 assert(_d==_d_piv);
271 assert(pivotsub->get_d()>0);
272 sub->stateless_copy(*pivotsub);
273 swing=UP; swinged=false;
274 assert(_d==sub->get_d());
275 break;
276 case UP:
277 assert(_d==sub->get_d());
278 assert(_d >= _d_piv);
279 if(pivot_top_avail) {assert(pivot_top->get_d()!=0); sub->stateless_copy(*pivot_top); _d=sub->get_d();}
280 swinged=false;
281 while(_d+parent::get_generalization_level()<=_n && _d<_cur_delta+_d_piv){
282 assert(_d==sub->get_d());
283 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;}
284 if(track) grandparent::_tracker->add(result,sub);
285 _d=sub->get_d(); swinged=true;
286 if(result>pivotval || (result==pivotval && _d<_d_piv)) {swing=RESTART; goto label;} // the DOS to OS difference: restart as soon as the pivot is beaten, equal value with fewer features included
287 }
288 if(swinged) {
289 assert(_d==sub->get_d());
291 pivot_top_avail=true;
292 pivot_top->stateless_copy(*sub);
293 }
294 swing=RETURN_DOWN;
295 } else {
296 pivot_top_avail=false;
297 swing=DOWN;
299 }
300 assert(_d==sub->get_d());
301 break;
302 case RETURN_DOWN:
303 assert(_d==sub->get_d());
304 assert(_d>_d_piv);
305 while(_d>_d_piv){
306 assert(_d==sub->get_d());
307 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;}
308 if(track) grandparent::_tracker->add(result,sub);
309 _d=sub->get_d();
310 if(result>pivotval || (result==pivotval && _d<_d_piv)) {swing=RESTART; goto label;} // the DOS to OS difference: restart as soon as the pivot is beaten, equal value with fewer features included
311 }
312 assert(_d==_d_piv);
313 assert(pivotsub->get_d()>0);
314 sub->stateless_copy(*pivotsub);
315 swing=DOWN; swinged=false;
317 assert(_d==sub->get_d());
318 break;
319 }
320 }
321 // the loop ends once the cycle depth exceeds _delta; the pivot is then the best subset found
322 if(pivotsub->get_d()!=0) {
323 sub->stateless_copy(*pivotsub);
324 result=pivotval;
326 msgbuf sos;
327 sos << "......................................." << std::endl;
328 sos << "Search_DOS() search finished. " << swatch << std::endl;
329 sos << "Search result: "<< std::endl << *sub << std::endl << "Criterion value: " << result << std::endl << std::endl << std::flush;
330 this->report(os,sos);
331 }
332 return true;
333 }
334 return false; // an empty pivot means no valid subset was ever adopted
335}
336
337
338} // namespace
339#endif // FSTSEARCHSEQDOS_H ///:~
Implements Dynamic_Oscillating_Search.
Definition search_seq_dos.hpp:97
void set_delta(const DIMTYPE delta)
oscillating cycle depth
Definition search_seq_dos.hpp:111
DIMTYPE get_delta() const
the currently configured oscillating cycle depth (see set_delta())
Definition search_seq_dos.hpp:112
Search_Sequential< RETURNTYPE, DIMTYPE, SUBSET, CRITERION, EVALUATOR > parent
base class alias
Definition search_seq_dos.hpp:99
parent::parent grandparent
the shared Search base (output detail, result tracker)
Definition search_seq_dos.hpp:100
DIMTYPE _delta
current oscillating cycle depth (max swing extent); see set_delta()
Definition search_seq_dos.hpp:136
Search_DOS(const PEvaluator evaluator)
evaluator must be non-null; initial_subset_enabled() is turned ON by construction (empty initial subs...
Definition search_seq_dos.hpp:104
std::shared_ptr< CRITERION > PCriterion
shared pointer to the criterion the search optimizes
Definition search_seq_dos.hpp:102
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_dos.hpp:139
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_dos.hpp:140
std::shared_ptr< SUBSET > PSubset
shared pointer to a feature subset
Definition search_seq_dos.hpp:103
std::shared_ptr< EVALUATOR > PEvaluator
shared pointer to the sequential-step evaluator
Definition search_seq_dos.hpp:101
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_dos.hpp:158
std::unique_ptr< SUBSET > pivotsub
the current pivot: the best-known subset (highest criterion value, smallest cardinality among equals)...
Definition search_seq_dos.hpp:138
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_dos.hpp:144
SwingPhase
Oscillating-cycle phase for the DOS swing state machine driving search()'s inline switch.
Definition search_seq_dos.hpp:129
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
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.