source: colin/branches/colin-dev-3a/src/SerialEvaluator.cpp @ 4775

Revision 4775, 8.4 KB checked in by wehart, 7 years ago (diff)

Renamed OptSolver::eval_mgr to have the same syntax as OptApplication?
objects (eval_mngr).

Changed the AsyncEvalF syntax to support priorities.

Moved OptApplication_Base::eval_mngr to a public method.

Misc edits in variable names for EvaluationManager? and SerialEvaluator?.

Updated ColinMain? to make help information have a cleaner format.

Line 
1/*  _________________________________________________________________________
2 *
3 *  COLIN: A Common Optimization Library INterface
4 *  Copyright (c) 2003, Sandia National Laboratories.
5 *  This software is distributed under the GNU Lesser General Public License.
6 *  For more information, see the README.html file in the top COLIN directory.
7 *  _________________________________________________________________________
8 */
9
10#include <colin/SerialEvaluator.h>
11
12using std::map;
13using std::list;
14
15
16namespace colin {
17
18namespace {
19const bool register_mngr
20    = EvalManagerFactory()->register_manager("SerialEvaluator",
21                                             &SerialEvaluator::create);
22}
23
24
25AppResponse
26SerialEvaluator::perform_evaluation(solverID_t solver_id, AppRequest &request)
27  { return request->compute_response(); }
28
29
30eval_id_t
31SerialEvaluator::queue_evaluation( solverID_t solver_id,
32                                   AppRequest &request,
33                                   evalPriority_t priority,
34                                   queueID_t queue_id )
35  {
36  EvalRecord er(request, this, queue_id);
37  queue[solver_id][queue_id][priority].push_back(er);
38  return er.evalID;
39  }
40
41
42eval_id_t
43SerialEvaluator::next_response( solverID_t solver_id,
44                                AppResponse &response,
45                                queueID_t queue_id )
46  {
47  // has a response already been computed?
48  map<solverID_t, list<ResponseRecord> >::iterator r_it
49      = responses.find(solver_id);
50  if ( r_it != responses.end() )
51    {
52    list<ResponseRecord>::iterator it = r_it->second.begin();
53    list<ResponseRecord>::iterator itEnd = r_it->second.end();
54    for ( ; it != itEnd; ++it )
55      {
56      if (( queue_id == 0 ) || ( queue_id == it->evalID.queue ))
57        {
58        ResponseRecord rr = *it;
59        r_it->second.erase(it);
60        if ( r_it->second.empty() )
61          { responses.erase(r_it); }
62       
63        response = rr.response;
64        return rr.evalID;
65        }
66      }
67    }
68
69  // Are there requests we can compute?
70  map<solverID_t, map<solverID_t, map<evalPriority_t, list<EvalRecord> > > >
71      ::iterator s_it = queue.find(solver_id);
72  if ( s_it == queue.end() )
73    {
74    response.reset();
75    return eval_id_t();
76    //EXCEPTION_MNGR(std::runtime_error, "SerialEvaluator::next_response(): "
77    //               "no evaluation requests in queue.");
78    }
79
80
81  // find the next evaluation we *should* perform
82  map<solverID_t, map<evalPriority_t, list<EvalRecord> > >::iterator ss_it;
83  if ( queue_id != 0 )
84    { ss_it = s_it->second.find(queue_id); }
85  else
86    {
87    int i = 2;
88    list<solverID_t> &nextQueue = next_queue_list[solver_id];
89    do
90      {
91      if ( nextQueue.empty() )
92        {
93        generate_next_queue_list(nextQueue, solver_id);
94        --i;
95        }
96     
97      ss_it = s_it->second.find(nextQueue.front());
98      nextQueue.pop_front();
99      } while (( ss_it == s_it->second.end() ) && ( i > 0 ));
100    }
101
102  if ( ss_it == s_it->second.end() )
103    {
104    response.reset();
105    return eval_id_t();
106    //EXCEPTION_MNGR(std::runtime_error, "SerialEvaluator::next_response(): "
107    //               "no evaluation requests on queue queues with "
108    //               "non-zero allocation.");
109    }
110
111  // perform the calculation and clean up the queue
112  ResponseRecord rr(ss_it->second.begin()->second.front());
113  ss_it->second.begin()->second.pop_front();
114  if ( ss_it->second.begin()->second.empty() )
115    {
116    ss_it->second.erase(ss_it->second.begin());
117    if ( ss_it->second.empty() )
118      {
119      s_it->second.erase(ss_it);
120      if ( s_it->second.empty() )
121        { queue.erase(s_it); }
122      }
123    }
124   
125  // return the result
126  response = rr.response;
127  return rr.evalID;
128  }
129
130
131/** If queue_id == 0, all queues are executed; otherwise, only the
132 *  specified queue is executed.
133 */
134void
135SerialEvaluator::synchronize(solverID_t solver_id, queueID_t queue_id)
136  {
137  map<solverID_t, map<solverID_t, map<evalPriority_t, list<EvalRecord> > > >
138      ::iterator s_it = queue.find(solver_id);
139  if ( s_it == queue.end() )
140    { return; }
141
142  // we are computing *everything*, so disregard ALL prioritization and
143  // just walk the tree computing everything
144  list<ResponseRecord> &responseList = responses[solver_id];
145  map<solverID_t, map<evalPriority_t, list<EvalRecord> > >::iterator ss_it;
146  map<evalPriority_t, list<EvalRecord> >::iterator p_it;
147  while ( ! s_it->second.empty() )
148    {
149    if ( queue_id == 0 )
150      { ss_it = s_it->second.begin(); }
151    else
152      {
153      ss_it = s_it->second.find(queue_id);
154      if ( ss_it == s_it->second.end() )
155        { return; }
156      }
157
158    while ( ! ss_it->second.empty() )
159      {
160      p_it = ss_it->second.begin();
161      while ( ! p_it->second.empty() )
162        {
163        responseList.push_back(p_it->second.front());
164        p_it->second.pop_front();
165        }
166      ss_it->second.erase(p_it);
167      }
168    s_it->second.erase(ss_it);
169    }
170  }
171
172
173/** If queue_id == 0, all queues are deleted; otherwise, only the
174 *  specified queue is deleted.
175 */
176void
177SerialEvaluator::clear_evaluations(solverID_t solver_id, queueID_t queue_id)
178  {
179  map<solverID_t, map<solverID_t, map<evalPriority_t, list<EvalRecord> > > >
180      ::iterator s_it = queue.find(solver_id);
181  if ( s_it == queue.end() )
182    { return; }
183
184  if ( queue_id == 0 )
185    { queue.erase(s_it); }
186  else
187    {
188    map<solverID_t, map<evalPriority_t, list<EvalRecord> > >::iterator ss_it
189        = s_it->second.find(queue_id);
190    if ( ss_it != s_it->second.end() )
191      { s_it->second.erase(ss_it); }
192    }
193  }
194
195
196void
197SerialEvaluator::generate_next_queue_list( std::list<solverID_t> &newList,
198                                           solverID_t solver_id )
199  {
200  // any allocation < 0.1% is equivalent to 0
201  const double EPS = 0.001;
202
203  newList.clear();
204  map<solverID_t, solver_info>::iterator s_it = solvers.find(solver_id);
205  if ( s_it == solvers.end() )
206    {
207    EXCEPTION_MNGR(std::runtime_error, "SerialEvaluator::"
208                   "generate_next_queue_list(): invalid solver id");
209    }
210
211  if ( s_it->second.queue_alloc.empty() )
212    {
213    EXCEPTION_MNGR(std::runtime_error, "SerialEvaluator::"
214                   "generate_next_queue_list(): queue allocation list is "
215                   "empty");
216    }
217 
218  map<queueID_t,double>::iterator it = s_it->second.queue_alloc.begin();
219  map<queueID_t,double>::iterator itEnd = s_it->second.queue_alloc.end();
220  double min = it->second;
221  for( ; it != itEnd; ++it )
222    {
223    if (( min > it->second ) && ( it->second > EPS ))
224      { min = it->second; }
225    }
226
227  if ( min <= EPS )
228    {
229    // we don't have a sensible allocation...  just use equal assignment
230    for ( it = s_it->second.queue_alloc.begin(); it != itEnd; ++it )
231      { newList.push_back(it->first); }
232    }
233  else
234    {
235    // the "best" thing to do here is to find least multiple that gives
236    // a rationally close to integer ratio among allocations.  Yippie
237    // this is more work than it is worth... so we will just get it to
238    // within a multiple of 4
239    std::map<queueID_t,double> tmp = s_it->second.queue_alloc;
240
241    double frac;
242    int mult = 1;
243
244    itEnd = tmp.end();
245    for ( it = tmp.begin(); it != itEnd; ++it )
246      {
247      if ( it->second < EPS )
248        { it->second = 0; }
249      else
250        {
251        it->second /= min;
252        frac = std::modf(it->second, 0);
253        if (( mult <= 2 ) && ( frac > .37 ) && ( frac < .63 ))
254          { mult = 2; }
255        else if (( mult < 4 ) && ( frac > 0.13 ) && ( frac < 0.87 ))
256          { mult = 4; }
257        }
258      }
259    if ( mult > 1 )
260      {
261      for ( it = tmp.begin(); it != itEnd; ++it )
262        { it->second *= mult; }
263      }
264    for ( it = tmp.begin(); it != itEnd; ++it )
265      {
266      newList.insert(newList.end(),
267                     static_cast<size_t>(std::floor(it->second+0.5)),
268                     it->first);
269      }
270    }
271 
272  // (pseudo) randomize the newList... believe it or not, this is O(N*log(N))
273  map<int, list<queueID_t> > sorted;
274  list<queueID_t>::iterator l_it = newList.begin();
275  list<queueID_t>::iterator l_itEnd = newList.end();
276  while( l_it != l_itEnd )
277    {
278    list<queueID_t> &tmp = sorted[std::rand()];
279    tmp.splice(tmp.end(), newList, l_it);
280    }
281  map<int, list<queueID_t> >::iterator sorted_it = sorted.begin();
282  map<int, list<queueID_t> >::iterator sorted_itEnd = sorted.end();
283  for ( ; sorted_it != sorted_itEnd; ++sorted_it )
284    { newList.splice(newList.end(), sorted_it->second); }
285  }
286
287} // namespace colin
Note: See TracBrowser for help on using the repository browser.