Salome HOME
CMake:
[modules/yacs.git] / doc / optimizer.rst
1
2 .. _optimizationplugin:
3
4 Definition of an algorithm for the OptimizerLoop
5 ==========================================================================
6 The definition of the optimization algorithm is done by way of plugin.
7 The plugin can be a C++ plugin implemented in a dynamic library (.so file) or a Python plugin implemented in a Python module (.py).
8 It is possible to implement two kinds of algorithm : synchronous or asynchronous.
9
10 Synchronous algorithm
11 --------------------------------------------------
12 In synchronous mode, the OptimizerLoop calls the algorithm to know what are the types of the input port (sample sent to the internal node), 
13 and of the output port (data returned by the internal node). Then it calls the algorithm to initialize
14 it. At each iteration, it calls the algorithm to produce new sample or to stop the iteration. Finally, it calls the algorithm
15 to finalize the optimization process.
16
17 A synchronous algorithm is implemented in a class derived from the base class OptimizerAlgSync with several methods that 
18 must be implemented and some optional methods (in C++ and in Python):
19
20 - **getTCForIn**, this method must return the YACS type of the input port of the internal node
21 - **getTCForOut**, this method must return the YACS type of the output port of the internal node
22 - **initialize** (optional), this method is called during the algorithm initialization
23 - **start**, this method is called at the beginning of iterations
24 - **takeDecision**, this method is called at each iteration
25 - **finish** (optional), this method is called to finish the algorithm at the end of the iteration process
26
27 In Python you need to implement another method:
28
29 - **setPool**, this method is used to set the data pool that is used to exchange data
30
31 C++ plugin example
32 ''''''''''''''''''''
33 Here is a small example of a C++ synchronous algorithm:
34
35 .. code-block:: cpp
36
37   #include <cmath>
38   
39   #include "OptimizerAlg.hxx"
40   
41   using namespace YACS::ENGINE;
42   
43   extern "C"
44   {
45     OptimizerAlgBase * createOptimizerAlgSyncExample(Pool * pool);
46   }
47   
48   class OptimizerAlgSyncExample : public OptimizerAlgSync
49     {
50     private:
51       int _idTest;
52       TypeCode *_tcIn;
53       TypeCode *_tcOut;
54     public:
55       OptimizerAlgSyncExample(Pool *pool);
56       virtual ~OptimizerAlgSyncExample();
57       TypeCode *getTCForIn() const;
58       TypeCode *getTCForOut() const;
59       void start();
60       void takeDecision();
61       void initialize(const Any *input) throw(YACS::Exception);
62       void finish();
63     };
64   
65   OptimizerAlgSyncExample::OptimizerAlgSyncExample(Pool *pool)
66     : OptimizerAlgSync(pool), _tcIn(0), _tcOut(0), _idTest(0)
67   {
68     _tcIn=new TypeCode(Double);
69     _tcOut=new TypeCode(Int);
70   }
71   
72   OptimizerAlgSyncExample::~OptimizerAlgSyncExample()
73   {
74     _tcIn->decrRef();
75     _tcOut->decrRef();
76   }
77   
78   //! Return the typecode of the expected input type
79   TypeCode * OptimizerAlgSyncExample::getTCForIn() const
80   {
81     return _tcIn;
82   }
83   
84   //! Return the typecode of the expected output type
85   TypeCode * OptimizerAlgSyncExample::getTCForOut() const
86   {
87     return _tcOut;
88   }
89   
90   //! Start to fill the pool with samples to evaluate
91   void OptimizerAlgSyncExample::start()
92   {
93     _idTest=0;
94     Any *val=AtomAny::New(1.2);
95     _pool->pushInSample(4,val);
96     val=AtomAny::New(3.4);
97     _pool->pushInSample(9,val);
98   }
99   
100   //! This method is called each time a sample has been evaluated.
101   /*!
102    *  It can either add new samples to evaluate in the pool, do nothing (wait
103    *  for more samples), or empty the pool to finish the evaluation.
104    */
105   void OptimizerAlgSyncExample::takeDecision()
106   {
107     if(_idTest==1)
108       {
109         Any *val=AtomAny::New(5.6);
110         _pool->pushInSample(16,val);
111         val=AtomAny::New(7.8);
112         _pool->pushInSample(25,val);
113         val=AtomAny::New(9. );
114         _pool->pushInSample(36,val);
115         val=AtomAny::New(12.3);
116         _pool->pushInSample(49,val);
117       }
118     else if(_idTest==4)
119       {
120         Any *val=AtomAny::New(45.6);
121         _pool->pushInSample(64,val);
122         val=AtomAny::New(78.9);
123         _pool->pushInSample(81,val);
124       }
125     else
126       {
127         Any *tmp= _pool->getCurrentInSample();
128         if(fabs(tmp->getDoubleValue()-45.6)<1.e-12)
129           _pool->destroyAll();
130       }
131     _idTest++;
132   }
133   
134   //! Optional method to initialize the algorithm.
135   /*!
136    *  For now, the parameter input is always NULL. It might be used in the
137    *  future to initialize an algorithm with custom data.
138    */
139   void OptimizerAlgSyncExample::initialize(const Any *input)
140     throw (YACS::Exception)
141   {
142   }
143   
144   /*!
145    *  Optional method called when the algorithm has finished, successfully or
146    *  not, to perform any necessary clean up.
147    */
148   void OptimizerAlgSyncExample::finish()
149   {
150   }
151   
152   //! Factory method to create the algorithm.
153   OptimizerAlgBase * createOptimizerAlgSyncExample(Pool *pool)
154   {
155     return new OptimizerAlgSyncExample(pool);
156   }
157
158
159 Here, the entry point in the dynamic library is the name of the factory function : createOptimizerAlgSyncExample
160 that returns an instance of the OptimizerAlgSyncExample class that implements the algorithm.
161
162 Python plugin example
163 ''''''''''''''''''''''
164 Here, the same example of a synchronous algorithm in Python::
165
166   import SALOMERuntime
167   
168   class myalgosync(SALOMERuntime.OptimizerAlgSync):
169     def __init__(self):
170       SALOMERuntime.OptimizerAlgSync.__init__(self, None)
171       r=SALOMERuntime.getSALOMERuntime()
172       self.tin=r.getTypeCode("double")
173       self.tout=r.getTypeCode("int")
174   
175     def setPool(self,pool):
176       """Must be implemented to set the pool"""
177       self.pool=pool
178   
179     def getTCForIn(self):
180       """returns typecode of type expected as Input"""
181       return self.tin
182   
183     def getTCForOut(self):
184       """returns typecode of type expected as Output"""
185       return self.tout
186   
187     def initialize(self,input):
188       """Optional method called on initialization. Do nothing here"""
189   
190     def start(self):
191       """Start to fill the pool with samples to evaluate."""
192       self.iter=0
193       self.pool.pushInSample(4,1.2)
194       self.pool.pushInSample(9,3.4)
195   
196     def takeDecision(self):
197       """ This method is called each time a sample has been evaluated. It can
198           either add new samples to evaluate in the pool, do nothing (wait for
199           more samples), or empty the pool to finish the evaluation.
200       """
201       currentId=self.pool.getCurrentId()
202   
203       if self.iter==1:
204         self.pool.pushInSample(16,5.6)
205         self.pool.pushInSample(25,7.8)
206         self.pool.pushInSample(36,9.)
207         self.pool.pushInSample(49,12.3)
208       elif self.iter==4:
209         self.pool.pushInSample(64,45.6)
210         self.pool.pushInSample(81,78.9)
211       else:
212         val=self.pool.getCurrentInSample()
213         if abs(val.getDoubleValue()-45.6) < 1.e-12:
214           self.pool.destroyAll()
215       self.iter=self.iter+1
216   
217     def finish(self):
218       """Optional method called when the algorithm has finished, successfully
219          or not, to perform any necessary clean up. Do nothing here"""
220
221 Here, the entry point in the Python module is directly the name of the class that implements the algorithm : myalgosync.
222
223
224 Asynchronous algorithm
225 --------------------------------------------------
226 In asynchronous mode, all is the same except that after the initialization phase, the OptimizerLoop calls the algorithm only one time
227 to start it in a separate thread.
228
229 An asynchronous algorithm is implemented in a class derived from the base class OptimizerAlgASync with several methods that 
230 must be implemented and some optional methods (in C++ and in Python):
231
232 - **getTCForIn**, this method must return the YACS type of the input port of the internal node
233 - **getTCForOut**, this method must return the YACS type of the output port of the internal node
234 - **initialize** (optional), this method is called during the algorithm initialization
235 - **startToTakeDecision**, this method is called to start the iteration process in a separate thread. It is the body of the algorithm.
236 - **finish** (optional), this method is called to finish the algorithm at the end of the iteration process
237
238 In Python you need to implement another method:
239
240 - **setPool**, this method is used to set the data pool that is used to exchange data
241
242 C++ plugin example
243 ''''''''''''''''''''
244 Here is a small example of a C++ asynchronous algorithm:
245
246 .. code-block:: cpp
247
248   #include "OptimizerAlg.hxx"
249   
250   using namespace YACS::ENGINE;
251   
252   extern "C"
253   {
254     OptimizerAlgBase * createOptimizerAlgASyncExample(Pool * pool);
255   }
256   
257   class OptimizerAlgASyncExample : public OptimizerAlgASync
258     {
259     private:
260       TypeCode * _tcIn;
261       TypeCode * _tcOut;
262     public:
263       OptimizerAlgASyncExample(Pool * pool);
264       virtual ~OptimizerAlgASyncExample();
265       TypeCode * getTCForIn() const;
266       TypeCode * getTCForOut() const;
267       void startToTakeDecision();
268     };
269   
270   OptimizerAlgASyncExample::OptimizerAlgASyncExample(Pool * pool)
271     : OptimizerAlgASync(pool), _tcIn(0), _tcOut(0)
272   {
273     _tcIn = new TypeCode(Double);
274     _tcOut = new TypeCode(Int);
275   }
276   
277   OptimizerAlgASyncExample::~OptimizerAlgASyncExample()
278   {
279     _tcIn->decrRef();
280     _tcOut->decrRef();
281   }
282   
283   //! Return the typecode of the expected input type
284   TypeCode *OptimizerAlgASyncExample::getTCForIn() const
285   {
286     return _tcIn;
287   }
288   
289   //! Return the typecode of the expected output type
290   TypeCode *OptimizerAlgASyncExample::getTCForOut() const
291   {
292     return _tcOut;
293   }
294   
295   //! This method is called only once to launch the algorithm.
296   /*!
297    *  It must first fill the pool with samples to evaluate and call
298    *  signalMasterAndWait() to block until a sample has been evaluated. When
299    *  returning from this method, it MUST check for an eventual termination
300    *  request (with the method isTerminationRequested()). If the termination
301    *  is requested, the method must perform any necessary cleanup and return
302    *  as soon as possible. Otherwise it can either add new samples to evaluate
303    *  in the pool, do nothing (wait for more samples), or empty the pool and
304    *  return to finish the evaluation.
305    */
306   void OptimizerAlgASyncExample::startToTakeDecision()
307   {
308     double val = 1.2;
309     for (int i=0 ; i<5 ; i++) {
310       // push a sample in the input of the slave node
311       _pool->pushInSample(i, AtomAny::New(val));
312       // wait until next sample is ready
313       signalMasterAndWait();
314       // check error notification
315       if (isTerminationRequested()) {
316         _pool->destroyAll();
317         return;
318       }
319   
320       // get a sample from the output of the slave node
321       Any * v = _pool->getCurrentOutSample();
322       val += v->getIntValue();
323     }
324   
325     // in the end destroy the pool content
326     _pool->destroyAll();
327   }
328   
329   //! Factory method to create the algorithm.
330   OptimizerAlgBase * createOptimizerAlgASyncExample(Pool * pool)
331   {
332     return new OptimizerAlgASyncExample(pool);
333   }
334
335
336 Here, the entry point in the dynamic library is the name of the factory function : createOptimizerAlgASyncExample
337 that returns an instance of the OptimizerAlgASyncExample class that implements the algorithm.
338
339 Python plugin example
340 ''''''''''''''''''''''''
341 Here is an example of an asynchronous algorithm implemented in Python::
342
343   import SALOMERuntime
344   
345   class myalgoasync(SALOMERuntime.OptimizerAlgASync):
346     def __init__(self):
347       SALOMERuntime.OptimizerAlgASync.__init__(self, None)
348       r=SALOMERuntime.getSALOMERuntime()
349       self.tin=r.getTypeCode("double")
350       self.tout=r.getTypeCode("int")
351   
352     def setPool(self,pool):
353       """Must be implemented to set the pool"""
354       self.pool=pool
355   
356     def getTCForIn(self):
357       """returns typecode of type expected as Input"""
358       return self.tin
359   
360     def getTCForOut(self):
361       """returns typecode of type expected as Output"""
362       return self.tout
363   
364     def startToTakeDecision(self):
365       """This method is called only once to launch the algorithm. It must
366          first fill the pool with samples to evaluate and call
367          self.signalMasterAndWait() to block until a sample has been
368          evaluated. When returning from this method, it MUST check for an
369          eventual termination request (with the method
370          self.isTerminationRequested()). If the termination is requested, the
371          method must perform any necessary cleanup and return as soon as
372          possible. Otherwise it can either add new samples to evaluate in the
373          pool, do nothing (wait for more samples), or empty the pool and
374          return to finish the evaluation.
375       """
376       val=1.2
377       for iter in xrange(5):
378         #push a sample in the input of the slave node
379         self.pool.pushInSample(iter,val)
380         #wait until next sample is ready
381         self.signalMasterAndWait()
382         #check error notification
383         if self.isTerminationRequested():
384           self.pool.destroyAll()
385           return
386   
387         #get a sample from the output of the slave node
388         currentId=self.pool.getCurrentId()
389         v=self.pool.getCurrentOutSample()
390         val=val+v.getIntValue()
391   
392       #in the end destroy the pool content
393       self.pool.destroyAll()
394
395 Here, the entry point in the Python module is directly the name of the class that implements the algorithm : myalgoasync.
396
397
398 C++ algorithm calling Python code
399 --------------------------------------------------
400
401 In some cases, it can be necessary to implement the algorithm as a C++ class but
402 nevertheless to call some Python code from this class. This is also possible with the
403 OptimizerLoop and even quite simple. To achieve this, your C++ class should inherit from
404 PyOptimizerAlgSync for a synchronous algorithm or from PyOptimizerAlgASync for an
405 asynchronous algorithm. The guidelines for developing the algorithm are the same as in
406 the C++ case, but you can also call any method from the Python C API. You don't need to
407 take care of the Python global interpreter lock or of thread states because this is
408 already done in the PyOptimizerAlg classes. An example of this kind of algorithm is the
409 class OpenTURNSScriptLauncher that can be found in the module OPENTURNS_SRC.