1 /* _________________________________________________________________________
2 *
3 * Acro: A Common Repository for Optimizers
4 * Copyright (c) 2008 Sandia Corporation.
5 * This software is distributed under the BSD License.
6 * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7 * the U.S. Government retains certain rights in this software.
8 * For more information, see the README.txt file in the top Acro directory.
9 * _________________________________________________________________________
10 */
11
12 #include <colin/Utilities.h>
13 #include <colin/solver/Base.h>
14 #include <colin/SolverMngr.h>
15 #include <colin/StatusCodes.h>
16 #include <colin/ApplicationMngr.h>
17 #include <colin/TinyXML_data_parser.h>
18 #include <utilib/TinyXML_helper.h>
19
20 using std::string;
21 using std::endl;
22
23 using std::list;
24 using std::set;
25 using std::map;
26 using std::make_pair;
27
28 using utilib::Any;
29
30
31 namespace colin {
32
33 struct Solver_Base::Data
34 {
Datacolin::Solver_Base::Data35 Data()
36 : construct_map(),
37 init_cache(false),
38 final_cache(true),
39 optimal_cache(true)
40 {}
41
42 struct CacheInfo {
CacheInfocolin::Solver_Base::Data::CacheInfo43 CacheInfo(bool reset_)
44 : has_data(false),
45 reset(reset_),
46 name(cache::ImplicitInterSolverCacheName),
47 points()
48 {}
49
50 bool has_data;
51 bool reset;
52 string name;
53 list<Any> points;
54 };
55
56 /// Container to hold the XML sub-element processors
57 /** Container to hold the XML sub-element processors
58 *
59 * This is a trick: boost::signals are not copyable, and thus cannot
60 * be put inside STL containers. However, if we first put the
61 * signal * into a non-copyable Any, and then put the Any into the
62 * STL * container, all is well and good in the world!
63 */
64 typedef map<string, Any> ConstructMap_t;
65
66 ///
67 ConstructMap_t construct_map;
68 ///
69 CacheInfo init_cache;
70 ///
71 CacheInfo final_cache;
72 ///
73 CacheInfo optimal_cache;
74 };
75
76
77 //============================================================================
78 //
79 //
Solver_Base()80 Solver_Base:: Solver_Base()
81 : solver_statistics(true),
82 data(new Data)
83 {
84 solver_status.solver_status=solver_unknown;
85 solver_status.model_status=model_status_unknown;
86 solver_status.termination_condition=termination_unknown;
87
88 properties.normalizeKeys() = true;
89
90 // register the local reset function
91 reset_signal.connect
92 (boost::bind(&Solver_Base::reset_Solver_Base, this));
93
94 // register the results generator
95 results_signal.connect
96 (boost::bind(&Solver_Base::cb_results, this, _1, _2));
97
98 // register the default XML components
99 register_construct("Problem").connect
100 (boost::bind(&Solver_Base::process_xml_problem, this, _1, _2));
101
102 register_construct("InitialPoint").connect
103 (boost::bind(&Solver_Base::process_xml_initialPoint, this, _1, _2));
104
105 register_construct("FinalPoint").connect
106 (boost::bind(&Solver_Base::process_xml_finalPoint, this, _1, _2));
107
108 //register_construct("OptimalPoint").connect
109 // (boost::bind(&Solver_Base::process_xml_optimalPoint, this, _1, _2));
110
111 register_construct("Options").connect
112 (boost::bind(&Solver_Base::process_xml_options, this, _1, _2));
113 }
114
115
116
117 /// Virtual destructor
~Solver_Base()118 Solver_Base::~Solver_Base()
119 {
120 delete data;
121 }
122
123
124 std::string
type() const125 Solver_Base::type() const
126 {
127 string ans = SolverMngr().get_solver_type(this);
128 if ( ans.empty() )
129 return define_solver_type();
130 else
131 return ans;
132 }
133
134
construct(TiXmlElement * root,bool describe)135 void Solver_Base::construct(TiXmlElement* root, bool describe)
136 {
137 if ( root == NULL )
138 return;
139 if ( describe )
140 {
141 root->SetAttribute("id", "string");
142
143 // Now, describe all registered sub-elements
144 Data::ConstructMap_t::iterator it = data->construct_map.begin();
145 Data::ConstructMap_t::iterator itEnd = data->construct_map.end();
146 for ( ; it != itEnd; ++it )
147 {
148 TiXmlElement* node = new TiXmlElement(it->first);
149 root->LinkEndChild(node);
150 it->second.expose<ConstructSignal_t>()(node, true);
151 }
152 return;
153 }
154
155 // Parse my attributes first
156 const char* name = root->Attribute("id");
157 if ( name != NULL )
158 SolverMngr().reregister_solver(this, name);
159
160 // Now, parse all valid sub-elements
161 Data::ConstructMap_t &init = data->construct_map;
162 Data::ConstructMap_t::iterator it;
163 Data::ConstructMap_t::iterator itEnd = init.end();
164 TiXmlElement* node = root->FirstChildElement();
165 while ( node != NULL )
166 {
167 it = init.find(node->ValueStr());
168 if ( it == itEnd )
169 EXCEPTION_MNGR(std::runtime_error, "Solver_Base::construct(): "
170 "No handler registered for " <<
171 utilib::get_element_info(node));
172
173 it->second.expose<ConstructSignal_t>()(node, false);
174 node = node->NextSiblingElement();
175 }
176 }
177
178
property(std::string name)179 utilib::Property& Solver_Base::property( std::string name )
180 {
181 utilib::PropertyDict::iterator it = properties.find(name);
182 if ( it == properties.end() )
183 EXCEPTION_MNGR(std::runtime_error, "Solver_Base::property(): "
184 "Attempt to retrieve nonexistent property, '"
185 << name << "'");
186
187 return it->second();
188 }
189
190
set_initial_points(PointSet ps)191 void Solver_Base::set_initial_points(PointSet ps)
192 {
193 initial_points = ps;
194
195 // This action overrides any intial point info specified through configure()
196 data->init_cache.name = "";
197 data->init_cache.points.clear();
198 data->init_cache.has_data = false;
199 }
200
201
add_initial_point(const utilib::AnyRef point)202 void Solver_Base::add_initial_point(const utilib::AnyRef point)
203 {
204 // If there was info specified through configure(), parse it now.
205 if ( data->init_cache.has_data )
206 initialize_xml_init_cache();
207
208 if ( point.is_type(typeid(AppResponse)) )
209 {
210 initial_points->insert(point.expose<AppResponse>());
211 return;
212 }
213
214 ApplicationHandle app = get_problem_handle();
215 if ( app.empty() )
216 EXCEPTION_MNGR(std::logic_error, "Solver_Base::add_initial_point(): "
217 "Cannot add initial domain points before setting "
218 "the problem.");
219
220 // Should we force an evaluation of the objective function here?
221 // Right now we are just inserting an empty response.
222 initial_points->insert
223 ( eval_mngr().perform_evaluation(app->set_domain(point)) );
224 }
225
226
get_final_points() const227 PointSet Solver_Base::get_final_points() const
228 {
229 return final_points;
230 }
231
232
233 //PointSet Solver_Base::get_optimal_points() const
234 //{
235 // return optimal_points;
236 //}
237
238
set_statistic(std::string name,utilib::Any value)239 void Solver_Base::set_statistic(std::string name, utilib::Any value)
240 {
241 if ( solver_statistics.exists(name) )
242 {
243 ucerr << "WARNING: overwriting solver statistic '"
244 << name << "' = " << solver_statistics[name] << endl
245 << " with new value = " << value << endl;
246 }
247 solver_statistics[name] = value;
248 }
249
250
251 //========================================================================
252 // Solver_Base protected members
253 //========================================================================
254
255 boost::signals2::signal<void(TiXmlElement*,bool)>&
register_construct(std::string element)256 Solver_Base::register_construct(std::string element)
257 {
258 Any& ans = data->construct_map[element];
259 if ( ans.empty() )
260 ans.set<ConstructSignal_t, Any::NonCopyable<ConstructSignal_t> >();
261 return ans.expose<ConstructSignal_t>();
262 }
263
264
265
266 //========================================================================
267 // Solver_Base private members
268 //========================================================================
269
270 void
reset_Solver_Base()271 Solver_Base::reset_Solver_Base()
272 {
273 // If we got cache configuration information through configure(), but
274 // are not being executed by the XML parser (i.e. the ExecuteMngr),
275 // we need to actually setup / initialize our caches based on that
276 // information. As initialize_xml_*_cache() clears the incoming
277 // data, this will only be executed te first time reset is called.
278 // Additionally, if the initial data was parsed using the
279 // SolverExecuteFunctor, SolverExecuteFunctor::execute() sets
280 // has_data to false, so nothing will happen here.
281
282 if ( data->init_cache.has_data )
283 initialize_xml_init_cache();
284
285 if ( data->final_cache.has_data )
286 initialize_xml_final_cache();
287
288 //if ( data->optimal_cache.has_data )
289 // initialize_xml_optimal_cache();
290 }
291
292 void
cb_results(utilib::PropertyDict & pd,int verbosity)293 Solver_Base::cb_results(utilib::PropertyDict &pd, int verbosity)
294 {
295 pd["problem"] = get_problem_handle()->describe(verbosity);
296 utilib::PropertyDict solver_pd = utilib::PropertyDict(true);
297 pd["solver"] = solver_pd;
298 solver_pd["name"] = define_solver_type();
299 solver_pd["status"] = solver_status.describe(verbosity);
300 solver_pd["statistics"] = solver_statistics;
301 }
302
303
304 void
process_xml_problem(TiXmlElement * node,bool describe)305 Solver_Base::process_xml_problem( TiXmlElement* node, bool describe )
306 {
307 if ( describe )
308 {
309 node->SetAttribute("id", "string");
310 return;
311 }
312
313 string problem;
314 utilib::get_string_attribute( node, "id", problem, "" );
315 if ( ! problem.empty() )
316 set_problem(ApplicationMngr().get_application(problem));
317 if ( get_problem_handle().empty() )
318 {
319 // Attempt to use the most recently created application
320 problem = ApplicationMngr().get_newest_application();
321 if ( ! problem.empty() )
322 set_problem( ApplicationMngr().get_application(problem) );
323 }
324
325 if ( ! get_problem_handle().empty() )
326 get_problem_handle()->initialize(node);
327 }
328
329 void
process_xml_initialPoint(TiXmlElement * node,bool describe)330 Solver_Base::process_xml_initialPoint( TiXmlElement* node, bool describe )
331 {
332 if ( describe )
333 return;
334
335 bool cache_specified = false;
336 cache_specified |= utilib::get_string_attribute
337 ( node, "cache", data->init_cache.name,
338 cache::ImplicitInterSolverCacheName );
339 cache_specified |= utilib::get_bool_attribute
340 (node, "clear", data->init_cache.reset, false);
341
342 data->init_cache.points.clear();
343 TiXmlElement* elt = node->FirstChildElement();
344 if ( elt == NULL )
345 {
346 // Convenience: if there are no child elements, but it has a
347 // single text child, assume it is a point
348 const char* text = node->GetText();
349 if ( text != NULL )
350 data->init_cache.points.push_back(parse_xml_data(node));
351 }
352 for( ; elt != NULL; elt = elt->NextSiblingElement() )
353 {
354 if ( elt->ValueStr().compare("Point") != 0 )
355 EXCEPTION_MNGR(std::runtime_error, "[Solver_Base] "
356 "process_xml_options(): invalid element "
357 << elt->ValueStr() << " in "
358 << utilib::get_element_info(elt));
359
360 data->init_cache.points.push_back(parse_xml_data(elt));
361 }
362
363 if ( ! cache_specified && ! data->init_cache.points.empty() )
364 data->init_cache.reset = true;
365
366 data->init_cache.has_data = true;
367 }
368
369
370 void
process_xml_finalPoint(TiXmlElement * node,bool describe)371 Solver_Base::process_xml_finalPoint( TiXmlElement* node, bool describe )
372 {
373 if ( describe )
374 return;
375
376 utilib::get_string_attribute
377 ( node, "cache", data->final_cache.name, "" );
378 utilib::get_bool_attribute
379 (node, "clear", data->final_cache.reset, true);
380
381 data->final_cache.has_data = true;
382 }
383
384
385 //void
386 //Solver_Base::process_xml_optimalPoint( TiXmlElement* node, bool describe )
387 //{
388 // if ( describe )
389 // return;
390 //
391 // utilib::get_string_attribute
392 // ( node, "cache", data->optimal_cache.name, ImplicitInterSolverCacheName );
393 // utilib::get_bool_attribute
394 // (node, "clear", data->optimal_cache.reset, true);
395 //
396 // data->optimal_cache.has_data = true;
397 //}
398
399
400 void
process_xml_options(TiXmlElement * node,bool describe)401 Solver_Base::process_xml_options( TiXmlElement* node, bool describe )
402 {
403 if ( describe )
404 {
405 TiXmlElement *opt = new TiXmlElement("Option");
406 opt->SetAttribute("name", "string");
407 node->LinkEndChild(opt);
408 return;
409 }
410
411 TiXmlElement* n = node->FirstChildElement();
412 for( ; n != NULL; n = n->NextSiblingElement() )
413 {
414 if ( n->ValueStr().compare("Option") != 0 )
415 EXCEPTION_MNGR(std::runtime_error, "[Solver_Base] "
416 "process_xml_options(): invalid element "
417 << n->ValueStr() << " in "
418 << utilib::get_element_info(n));
419 string name = "";
420 utilib::get_string_attribute(n, "name", name);
421 properties[name] = parse_xml_data(n);
422 }
423 }
424
425
426 void
initialize_xml_init_cache()427 Solver_Base::initialize_xml_init_cache()
428 {
429 //
430 // Now we actually set up everything for the initial cache...
431 //
432 if ( ! data->init_cache.name.empty() )
433 {
434 CacheHandle cache = CacheFactory().get_cache(data->init_cache.name);
435 if ( cache.empty() )
436 {
437 // register a new instance of the cache
438 cache = PointSet().operator->()->get_handle();
439 CacheFactory().register_cache(cache, data->init_cache.name);
440 }
441
442 if ( data->init_cache.reset )
443 cache->clear();
444
445 initial_points = PointSet(cache);
446 }
447
448 // Must set has_data to false, or add_initial_point will infinitely recurse
449 data->init_cache.has_data = false;
450
451 list<Any> &points = data->init_cache.points;
452 for (; ! points.empty(); points.pop_front() )
453 add_initial_point(points.front());
454
455 data->init_cache = Data::CacheInfo(false);
456 }
457
458
459 void
initialize_xml_final_cache()460 Solver_Base::initialize_xml_final_cache()
461 {
462 CacheHandle cache = CacheFactory().get_cache(data->final_cache.name);
463 if ( data->final_cache.reset && ! cache.empty() )
464 {
465 // Don't clear the cache (we might be using it for our input
466 // points!). Rather, unregister it and let the CacheHandle take
467 // care of deleting it when the last one falls out of scope.
468 CacheFactory().unregister_cache(data->final_cache.name);
469 cache = CacheHandle();
470 }
471 if ( cache.empty() )
472 {
473 // defer to the default cache used by PointSets
474 cache = PointSet().operator->()->get_handle();
475 CacheFactory().register_cache(cache, data->final_cache.name);
476 }
477 final_points = PointSet(cache);
478 data->final_cache = Data::CacheInfo(true);
479 }
480
481
482 //void
483 //Solver_Base::initialize_xml_optimal_cache()
484 //{
485 // CacheHandle cache = CacheFactory().get_cache(data->optimal_cache.name);
486 // if ( data->optimal_cache.reset && ! cache.empty() )
487 // {
488 // // Don't clear the cache (we might be using it for our input
489 // // points!). Rather, unregister it and let the CacheHandle take
490 // // care of deleting it when the last one falls out of scope.
491 // CacheFactory().unregister_cache(data->optimal_cache.name);
492 // cache = CacheHandle();
493 // }
494 // if ( cache.empty() )
495 // {
496 // // defer to the default cache used by PointSets
497 // cache = PointSet().operator->()->get_handle();
498 // CacheFactory().register_cache(cache, data->optimal_cache.name);
499 // }
500 // optimal_points = PointSet(cache);
501 // data->optimal_cache = Data::CacheInfo(true);
502 //}
503
504 } // namespace colin
505