Salome HOME
cbafe892f19d618b3814d377bd996a14999d7a74
[modules/yacs.git] / src / engine / OptimizerLoop.cxx
1 //  Copyright (C) 2006-2008  CEA/DEN, EDF R&D
2 //
3 //  This library is free software; you can redistribute it and/or
4 //  modify it under the terms of the GNU Lesser General Public
5 //  License as published by the Free Software Foundation; either
6 //  version 2.1 of the License.
7 //
8 //  This library is distributed in the hope that it will be useful,
9 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 //  Lesser General Public License for more details.
12 //
13 //  You should have received a copy of the GNU Lesser General Public
14 //  License along with this library; if not, write to the Free Software
15 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 #include "OptimizerLoop.hxx"
20 #include "OutputPort.hxx"
21
22 #include <iostream>
23
24 using namespace YACS::ENGINE;
25 using namespace std;
26
27 const char FakeNodeForOptimizerLoop::NAME[]="thisIsAFakeNode";
28
29 const int OptimizerLoop::NOT_RUNNING_BRANCH_ID=-1973012217;
30
31 const char OptimizerLoop::NAME_OF_FILENAME_INPUT[]="FileNameInitAlg";
32
33 const char OptimizerLoop::NAME_OF_OUT_POOL_INPUT[]="retPortForOutPool";
34
35 OptimizerAlgStandardized::OptimizerAlgStandardized(Pool *pool, OptimizerAlgBase *alg)
36   : OptimizerAlgSync(pool),_algBehind(alg),_threadInCaseOfNotEvent(0)
37 {
38   if(_algBehind)
39     _algBehind->incrRef();
40 }
41
42 OptimizerAlgStandardized::~OptimizerAlgStandardized()
43 {
44   if(_algBehind)
45     _algBehind->decrRef();
46 }
47
48 TypeCode *OptimizerAlgStandardized::getTCForIn() const
49 {
50   return _algBehind->getTCForIn();
51 }
52
53 TypeCode *OptimizerAlgStandardized::getTCForOut() const
54 {
55   return _algBehind->getTCForOut();
56 }
57
58 void OptimizerAlgStandardized::setAlgPointer(OptimizerAlgBaseFactory algFactory)
59 {
60   if(_algBehind)
61     _algBehind->decrRef();
62   _algBehind=algFactory(_pool);
63 }
64
65 void OptimizerAlgStandardized::parseFileToInit(const std::string& fileName)
66 {
67   _algBehind->parseFileToInit(fileName);
68 }
69
70 void OptimizerAlgStandardized::initialize(const Any *input) throw (Exception)
71 {
72   _algBehind->initialize(input);
73 }
74
75 void OptimizerAlgStandardized::takeDecision()
76 {
77   switch(_algBehind->getType())
78     {
79     case EVENT_ORIENTED:
80       {
81         ((OptimizerAlgSync *) _algBehind)->takeDecision();
82         break;
83       }
84     case NOT_EVENT_ORIENTED:
85       {   
86         _condition.notifyOneSync();
87         break;
88       }
89     default:
90       throw Exception("Unrecognized type of algorithm. Only 2 types are available : EVENT_ORIENTED or NOT_EVENT_ORIENTED.");
91     }
92 }
93
94 void OptimizerAlgStandardized::finish()
95 {
96   _algBehind->finish();
97 }
98
99 void OptimizerAlgStandardized::start()
100 {
101   switch(_algBehind->getType())
102     {
103     case EVENT_ORIENTED:
104       {
105         ((OptimizerAlgSync *) _algBehind)->start();
106         break;
107       }
108     case NOT_EVENT_ORIENTED:
109       {
110         void **stackForNewTh= new void* [2];
111         stackForNewTh[0]=(void *) ((OptimizerAlgASync *)(_algBehind));//In case of virtual inheritance
112         stackForNewTh[1]=(void *) &_condition;
113         _threadInCaseOfNotEvent=new ::YACS::BASES::Thread(threadFctForAsync,stackForNewTh);
114         _condition.waitForAWait();
115         break;
116       }
117     default:
118       throw Exception("Unrecognized type of algorithm. Only 2 types are available : EVENT_ORIENTED or NOT_EVENT_ORIENTED.");
119     }
120 }
121
122 void *OptimizerAlgStandardized::threadFctForAsync(void* ownStack)
123 {
124   void **ownStackCst=(void **)ownStack;
125   OptimizerAlgASync *alg=(OptimizerAlgASync *)ownStackCst[0];
126   ::YACS::BASES::DrivenCondition *cond=(::YACS::BASES::DrivenCondition *)ownStackCst[1];
127   delete [] ownStackCst;
128   alg->startToTakeDecision(cond);
129 }
130
131 FakeNodeForOptimizerLoop::FakeNodeForOptimizerLoop(OptimizerLoop *loop, bool normal, unsigned reason):ElementaryNode(NAME),_loop(loop),
132                                                                                                       _normal(normal),
133                                                                                                       _reason(reason)
134 {
135   _state=YACS::TOACTIVATE;
136   _father=_loop->getFather();
137 }
138
139 FakeNodeForOptimizerLoop::FakeNodeForOptimizerLoop(const FakeNodeForOptimizerLoop& other):ElementaryNode(other),_loop(0),
140                                                                                           _normal(false)
141
142 }
143
144 Node *FakeNodeForOptimizerLoop::simpleClone(ComposedNode *father, bool editionOnly) const
145 {
146   return new FakeNodeForOptimizerLoop(*this);
147 }
148
149 void FakeNodeForOptimizerLoop::exForwardFailed()
150 {
151   _loop->exForwardFailed();
152   FakeNodeForOptimizerLoop *normallyThis=_loop->_nodeForSpecialCases;
153   _loop->_nodeForSpecialCases=0;
154   delete normallyThis;
155 }
156
157 void FakeNodeForOptimizerLoop::exForwardFinished()
158 {
159   _loop->exForwardFinished();
160   FakeNodeForOptimizerLoop *normallyThis=_loop->_nodeForSpecialCases;
161   _loop->_nodeForSpecialCases=0;
162   delete normallyThis;
163 }
164
165 void FakeNodeForOptimizerLoop::execute()
166 {
167   if(!_normal)
168     {
169       string what;
170       if(_reason==ALG_WITHOUT_START_CASES)
171         what="Alg initialization of optimizerNode with name "; what+=_loop->getName(); what+=" returns no new case(s) to launch";
172       throw Exception(what);
173     }
174 }
175
176 void FakeNodeForOptimizerLoop::aborted()
177 {
178   _loop->_state=YACS::ERROR;
179 }
180
181 void FakeNodeForOptimizerLoop::finished()
182 {
183   
184 }
185
186 OptimizerLoop::OptimizerLoop(const std::string& name, const std::string& algLibWthOutExt,
187                              const std::string& symbolNameToOptimizerAlgBaseInstanceFactory,
188                              bool algInitOnFile) throw(Exception)
189   try : DynParaLoop(name,Runtime::_tc_string),_loader(algLibWthOutExt),_algInitOnFile(algInitOnFile),
190         _portForInitFile(NAME_OF_FILENAME_INPUT,this,Runtime::_tc_string),
191         _alg(new OptimizerAlgStandardized(&_myPool,0)),_convergenceReachedWithOtherCalc(false),
192         _retPortForOutPool(NAME_OF_OUT_POOL_INPUT,this,Runtime::_tc_string),
193         _nodeForSpecialCases(0),_symbol(symbolNameToOptimizerAlgBaseInstanceFactory)
194 {
195   OptimizerAlgBaseFactory algFactory=(OptimizerAlgBaseFactory)_loader.getHandleOnSymbolWithName(_symbol);
196   if(!algFactory)
197     throw Exception("Problem during library loading.");
198   _alg->setAlgPointer(algFactory);
199   _splittedPort.edSetType(_alg->getTCForIn());
200   _retPortForOutPool.edSetType(_alg->getTCForOut());
201 }
202 catch(Exception& e)
203 {
204 }
205
206 OptimizerLoop::OptimizerLoop(const OptimizerLoop& other, ComposedNode *father, bool editionOnly):
207   DynParaLoop(other,father,editionOnly),_algInitOnFile(other._algInitOnFile),_loader(other._loader.getLibNameWithoutExt()),_convergenceReachedWithOtherCalc(false),
208   _alg(new OptimizerAlgStandardized(&_myPool,0)),_portForInitFile(other._portForInitFile,this),_retPortForOutPool(other._retPortForOutPool,this),_nodeForSpecialCases(0),_symbol(other._symbol)
209 {
210   OptimizerAlgBaseFactory algFactory=(OptimizerAlgBaseFactory)_loader.getHandleOnSymbolWithName(_symbol);
211   _alg->setAlgPointer(algFactory);
212   _splittedPort.edSetType(_alg->getTCForIn());
213   _retPortForOutPool.edSetType(_alg->getTCForOut());
214 }
215
216 OptimizerLoop::~OptimizerLoop()
217 {
218   _alg->decrRef();
219   cleanDynGraph();
220   cleanInterceptors();
221 }
222
223 Node *OptimizerLoop::simpleClone(ComposedNode *father, bool editionOnly) const
224 {
225   return new OptimizerLoop(*this,father,editionOnly);
226 }
227
228 void OptimizerLoop::init(bool start)
229 {
230   DynParaLoop::init(start);
231   _portForInitFile.exInit(start);
232   _convergenceReachedWithOtherCalc=false;
233   cleanDynGraph();
234   cleanInterceptors();
235 }
236
237 void OptimizerLoop::exUpdateState()
238 {
239   if(_state == YACS::DISABLED)
240     return;
241   if(_inGate.exIsReady())
242     {
243       _state=YACS::TOACTIVATE;
244       //internal graph update
245       int i;
246       int nbOfBr=_nbOfBranches.getIntValue();
247       if(nbOfBr==0)
248         {
249           delete _nodeForSpecialCases;
250           _nodeForSpecialCases=new FakeNodeForOptimizerLoop(this,getAllOutPortsLeavingCurrentScope().empty(),FakeNodeForOptimizerLoop::NO_BRANCHES);
251           return;
252         }
253       
254       if(_portForInitFile.isEmpty())
255         {
256           delete _nodeForSpecialCases;
257           _nodeForSpecialCases=new FakeNodeForOptimizerLoop(this,getAllOutPortsLeavingCurrentScope().empty(),FakeNodeForOptimizerLoop::NO_ALG_INITIALIZATION);
258           return;
259         }
260       _execNodes.resize(nbOfBr);
261       _execIds.resize(nbOfBr);
262       if(_initNode)
263         {
264           _execInitNodes.resize(nbOfBr);
265           _initNodeUpdated.resize(nbOfBr);
266           for(i=0;i<nbOfBr;i++)
267             _initNodeUpdated[i]=false;
268         }
269       for(i=0;i<nbOfBr;i++)
270         {
271           _execIds[i]=NOT_RUNNING_BRANCH_ID;
272           _execNodes[i]=_node->clone(this,false);
273           if(_initNode)
274             _execInitNodes[i]=_initNode->clone(this,false);
275           prepareInputsFromOutOfScope(i);
276         }
277       initInterceptors(nbOfBr);
278       _alg->parseFileToInit(_portForInitFile.getValue()->getStringValue());
279       _alg->start();
280       int id;
281       unsigned char priority;
282       Any *val=_myPool.getNextSampleWithHighestPriority(id,priority);
283       if(!val)
284         {
285           delete _nodeForSpecialCases;
286           _nodeForSpecialCases=new FakeNodeForOptimizerLoop(this,getAllOutPortsLeavingCurrentScope().empty(),FakeNodeForOptimizerLoop::ALG_WITHOUT_START_CASES);
287           return;
288         }
289       launchMaxOfSamples(true);
290     }
291 }
292
293 int OptimizerLoop::getNumberOfInputPorts() const
294 {
295   return DynParaLoop::getNumberOfInputPorts()+2;
296 }
297
298 InputPort *OptimizerLoop::getInputPort(const std::string& name) const throw(Exception)
299 {
300   if(name==NAME_OF_FILENAME_INPUT)
301     return (InputPort *)&_portForInitFile;
302   else if(name==NAME_OF_OUT_POOL_INPUT)
303     return (InputPort *)&_retPortForOutPool;
304   else
305     return DynParaLoop::getInputPort(name);
306 }
307
308 std::list<InputPort *> OptimizerLoop::getSetOfInputPort() const
309 {
310   list<InputPort *> ret=DynParaLoop::getSetOfInputPort();
311   ret.push_back((InputPort *)&_portForInitFile);
312   ret.push_back((InputPort *)&_retPortForOutPool);
313   return ret;
314 }
315
316 std::list<InputPort *> OptimizerLoop::getLocalInputPorts() const
317 {
318   list<InputPort *> ret=DynParaLoop::getLocalInputPorts();
319   ret.push_back((InputPort *)&_portForInitFile);
320   ret.push_back((InputPort *)&_retPortForOutPool);
321   return ret;
322 }
323
324 void OptimizerLoop::selectRunnableTasks(std::vector<Task *>& tasks)
325 {
326 }
327
328 void OptimizerLoop::getReadyTasks(std::vector<Task *>& tasks)
329 {
330   if(!_node)
331     return;
332   if(_state==YACS::TOACTIVATE || _state==YACS::ACTIVATED)
333     {
334       if(_nodeForSpecialCases)
335         {
336           _nodeForSpecialCases->getReadyTasks(tasks);
337           return ;
338         }
339       for(vector<Node *>::iterator iter=_execNodes.begin();iter!=_execNodes.end();iter++)
340         (*iter)->getReadyTasks(tasks);
341       for(vector<Node *>::iterator iter2=_execInitNodes.begin();iter2!=_execInitNodes.end();iter2++)
342         (*iter2)->getReadyTasks(tasks);
343     }
344     return;
345   
346 }
347
348 YACS::Event OptimizerLoop::updateStateOnFinishedEventFrom(Node *node)
349 {
350   unsigned int id;
351   switch(getIdentityOfNotifyerNode(node,id))
352     {
353     case INIT_NODE:
354       _execNodes[id]->exUpdateState();
355       _nbOfEltConsumed++;
356       break;
357     case WORK_NODE:
358       if(_state==YACS::DONE)//This case happend when alg has reached its convergence whereas other calculations still compute.
359         {
360           if(isFullyLazy())
361             _condForCompletenessB4Relaunch.wait();
362           return YACS::NOEVENT;
363         }
364       _myPool.putOutSampleAt(_execIds[id],_interceptorsForOutPool[id]->getValue());
365       _myPool.setCurrentId(_execIds[id]);
366       _alg->takeDecision();
367       _myPool.destroyCurrentCase();
368       if(_myPool.empty())
369         {
370           pushValueOutOfScopeForCase(id);
371           _execIds[id]=NOT_RUNNING_BRANCH_ID;
372           if(!isFullyLazy())// This case happens when the hand is returned to continue, whereas some other are working in parallel for nothing.
373             _convergenceReachedWithOtherCalc=true;
374           _state=YACS::DONE;
375           return YACS::FINISH;
376         }
377       _execIds[id]=NOT_RUNNING_BRANCH_ID;
378       int newId;
379       unsigned char priority;
380       Any *val=_myPool.getNextSampleWithHighestPriority(newId, priority);
381       if(!val)
382         {
383           bool isFinished=true;
384           for(int i=0;i<_execIds.size() and isFinished;i++)
385             isFinished=(_execIds[i]==NOT_RUNNING_BRANCH_ID);
386           if(isFinished)
387             {
388               std::cerr <<"OptimizerLoop::updateStateOnFinishedEventFrom: Alg has not inserted more cases whereas last element has been calculated !" << std::endl;
389               exForwardFailed();
390               _state=YACS::INTERNALERR;
391               return YACS::FINISH;
392             }
393           return YACS::NOEVENT;
394         }
395       launchMaxOfSamples(false);
396     }
397   return YACS::NOEVENT;
398 }
399
400 void OptimizerLoop::checkNoCyclePassingThrough(Node *node) throw(Exception)
401 {
402 }
403
404 void OptimizerLoop::buildDelegateOf(InPort * & port, OutPort *initialStart, const std::list<ComposedNode *>& pointsOfView)
405 {
406   DynParaLoop::buildDelegateOf(port,initialStart,pointsOfView);
407   if(port==&_retPortForOutPool)
408     throw Exception("OptimizerLoop::buildDelegateOf : uncorrect OptimizerLoop link : out pool port must be linked within the scope of OptimizerLoop node it belongs to.");
409 }
410
411 void OptimizerLoop::buildDelegateOf(std::pair<OutPort *, OutPort *>& port, InPort *finalTarget, const std::list<ComposedNode *>& pointsOfView)
412 {
413   DynParaLoop::buildDelegateOf(port,finalTarget,pointsOfView);
414   string typeOfPortInstance=(port.first)->getNameOfTypeOfCurrentInstance();
415   if(typeOfPortInstance!=OutputPort::NAME)
416     throw Exception("OptimizerLoop::buildDelegateOf : not implemented for DS because not specified ");
417 }
418
419 void OptimizerLoop::checkControlDependancy(OutPort *start, InPort *end, bool cross,
420                                            std::map < ComposedNode *,  std::list < OutPort * >, SortHierarc >& fw,
421                                            std::vector<OutPort *>& fwCross,
422                                            std::map< ComposedNode *, std::list < OutPort *>, SortHierarc >& bw,
423                                            LinkInfo& info) const
424 {
425   if(end==&_retPortForOutPool)
426     fw[(ComposedNode *)this].push_back(start);
427   else
428     DynParaLoop::checkControlDependancy(start,end,cross,fw,fwCross,bw,info);
429 }
430
431 void OptimizerLoop::checkCFLinks(const std::list<OutPort *>& starts, InputPort *end, unsigned char& alreadyFed, bool direction, LinkInfo& info) const
432 {
433   if(end==&_retPortForOutPool)
434     solveObviousOrDelegateCFLinks(starts,end,alreadyFed,direction,info);
435   else
436     DynParaLoop::checkCFLinks(starts,end,alreadyFed,direction,info);
437 }
438
439 void OptimizerLoop::cleanInterceptors()
440 {
441   //the destruction of interceptors whereas some running nodes can push value on them can lead to SIG SEGV.
442   if(!_execNodes.empty())
443     {
444       if(_convergenceReachedWithOtherCalc)
445         {
446           cout << "Waiting completion of last other useless cases." << endl;
447           _condForCompletenessB4Relaunch.waitForAWait();
448         }
449     }
450   // At this point all garanties taken let's clean all.
451   map<InputPort *,vector<InputPort *> >::iterator iter=_interceptors.begin();
452   for(;iter!=_interceptors.end();iter++)
453     for(vector<InputPort *>::iterator iter2=(*iter).second.begin();iter2!=(*iter).second.end();iter2++)
454       delete (*iter2);
455   _interceptors.clear();
456   for(vector<AnyInputPort *>::iterator iter3=_interceptorsForOutPool.begin();iter3!=_interceptorsForOutPool.end();iter3++)
457     delete (*iter3);
458   _interceptorsForOutPool.clear();
459 }
460
461 void OptimizerLoop::launchMaxOfSamples(bool first)
462 {
463   int id;
464   unsigned char priority;
465   Any *val;
466   unsigned i;
467   for(val=_myPool.getNextSampleWithHighestPriority(id,priority);!isFullyBusy(i) && val;val=_myPool.getNextSampleWithHighestPriority(id,priority))
468     {
469       _execIds[i]=id;
470       _myPool.markIdAsInUse(id);
471       if(_initNode)
472         {
473           if(!_initNodeUpdated[i])
474             {
475               putValueOnBranch(val,i,first);
476               _execInitNodes[i]->exUpdateState();
477               _initNodeUpdated[i]=true;
478             }
479           else
480             {
481               putValueOnBranch(val,i,first);
482               _execNodes[i]->exUpdateState();
483               _nbOfEltConsumed++;
484             }
485         }
486       else
487         {
488           if(!first)
489             _execNodes[i]->init(first);
490           putValueOnBranch(val,i,first);
491           _execNodes[i]->exUpdateState();
492           _nbOfEltConsumed++;
493         }
494     }
495 }
496
497 bool OptimizerLoop::isFullyLazy() const
498 {
499   bool isLazy=true;
500   for(unsigned i=0;i<_execIds.size() and isLazy;i++)
501     isLazy=(_execIds[i]==NOT_RUNNING_BRANCH_ID);
502   return isLazy;
503 }
504
505 /*!
506  * Returns if a dynamic branch is available.
507  * \param branchId Out param. Only usable if returned value is equal to \b false.
508  */
509 bool OptimizerLoop::isFullyBusy(unsigned& branchId) const
510 {
511   bool isFinished=true;
512   unsigned i;
513   for(i=0;i<_execIds.size() and isFinished;i++)
514     isFinished=(_execIds[i]!=NOT_RUNNING_BRANCH_ID);
515   if(!isFinished)
516     branchId=i-1;
517   return isFinished;
518 }
519
520 /*!
521  * Perform initialization of interceptors. \b WARNING _execNodes have to be created before.
522  */
523 void OptimizerLoop::initInterceptors(unsigned nbOfBr)
524 {
525   //For all classical outputports leaving 'this'
526   set<OutPort *> portsToIntercept=getAllOutPortsLeavingCurrentScope();
527   for(set<OutPort *>::iterator iter=portsToIntercept.begin();iter!=portsToIntercept.end();iter++)
528     {
529       OutputPort *portC=(OutputPort *)(*iter);//Warrantied by OptimizerLoop::buildDelegateOf
530       const set<InputPort *>& links=portC->getSetOfPhyLinks();
531       for(set<InputPort *>::const_iterator iter2=links.begin();iter2!=links.end();iter2++)
532         {
533 #ifdef NOCOVARIANT
534           InputPort *reprCur=dynamic_cast<InputPort *>((*iter2)->getPublicRepresentant());
535 #else
536           InputPort *reprCur=(*iter2)->getPublicRepresentant();
537 #endif
538           if(!isInMyDescendance(reprCur->getNode()))
539             {//here we've got an out of scope link : Let's intercept it
540               if(_interceptors.find(reprCur)==_interceptors.end())
541                 {
542                   _interceptors[reprCur].resize(nbOfBr);
543                   for(unsigned i=0;i<nbOfBr;i++)
544                     {
545                       OutputPort *portExecC=(OutputPort *)_execNodes[i]->getOutputPort(_node->getOutPortName(portC));
546                       InputPort *clone=reprCur->clone(0);
547                       _interceptors[reprCur][i]=clone;
548                       portExecC->edAddInputPort(clone);
549                     }
550                 }
551               else
552                 {
553                   for(unsigned i=0;i<nbOfBr;i++)
554                     {
555                       OutputPort *portExecC=(OutputPort *)_execNodes[i]->getOutputPort(_node->getOutPortName(portC));
556                       portExecC->edAddInputPort(_interceptors[reprCur][i]);
557                     }
558                 }
559             }
560         }
561     }
562   // For out pool
563   _interceptorsForOutPool.resize(nbOfBr);
564   set< OutPort * > links=_retPortForOutPool.edSetOutPort();
565   for(unsigned i=0;i<nbOfBr;i++)
566     _interceptorsForOutPool[i]=(AnyInputPort *)_retPortForOutPool.clone(this);
567   for(set<OutPort *>::iterator iter2=links.begin();iter2!=links.end();iter2++)
568     for(unsigned j=0;j<nbOfBr;j++)
569       {
570         OutPort *portExec;
571         Node *whatType=isInMyDescendance((*iter2)->getNode());
572         if(whatType==_node)
573           {
574             portExec=_execNodes[j]->getOutPort(_node->getOutPortName(*iter2));
575             portExec->addInPort(_interceptorsForOutPool[j]);
576           }
577         else if(whatType==_initNode && whatType!=0)//This case should never happend. Useless !
578           {
579             portExec=_execInitNodes[j]->getOutPort(_node->getOutPortName(*iter2));
580             portExec->addInPort(_interceptorsForOutPool[j]);
581           }
582       }
583 }
584
585 /*!
586  * Typically called when _alg has decided that convergence has been reached. In this case the links leaving the current scope are activated and filled
587  * with value of the branch specified by 'branchId' that is the branch in which the convergence has been reached.
588  */
589 void OptimizerLoop::pushValueOutOfScopeForCase(unsigned branchId)
590 {
591   map<InputPort *, std::vector<InputPort *> >::iterator iter;
592   for(iter=_interceptors.begin();iter!=_interceptors.end();iter++)
593     (*iter).first->put((*iter).second[branchId]->get());
594 }
595