Salome HOME
Copyright update 2022
[modules/yacs.git] / src / pmml / PMMLlib.cxx
1 // Copyright (C) 2013-2022 CEA/DEN
2 //
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU Lesser General Public License as published
5 // by the Free Software Foundation, either version 3 of the License, or any
6 // later version.
7 //
8 // This program 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
11 // GNU Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public License
14 // along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16 /*!
17   \file   PMMLlib.cxx
18   \author Incka
19   \date   Wed Nov 20 11:04:17 2013
20
21   \brief  Implémentation de la classe PMMLlib
22
23  */
24
25 // includes Salomé
26 #include "PMMLlib.hxx"
27
28 // includes C
29 #include <stdlib.h>
30
31 // includes C++
32 #include <cstdlib>
33 #include <iostream>
34 #include <fstream>
35 #include <sstream>
36
37 using namespace std;
38
39 namespace PMMLlib
40 {
41   
42 //**************************************************************
43 //                                                             *
44 //                                                             *
45 //                                                             *
46 //  méthodes communes à tous les types de modèles              *
47 //                                                             *
48 //                                                             *
49 //                                                             *
50 //**************************************************************
51   
52 /**
53  * Constructor to read a PMML file.
54  * @param file Name of the PMML file to read
55  * @param log Flag to print logs or not
56  */
57 PMMLlib::PMMLlib(std::string file,bool log) : 
58             _log(log),
59             _pmmlFile(file),
60             _doc(NULL),
61             _rootNode(NULL),
62             _currentNode(NULL),
63             _nbModels(0),
64             _currentModelName(""),
65             _currentModelType(kUNDEFINED)   
66 {
67     try
68     {
69         xmlKeepBlanksDefault(0);
70         xmlInitParser();
71         _doc = xmlParseFile(_pmmlFile.c_str());         
72         if ( _doc != NULL )
73         {
74             _rootNode = xmlDocGetRootElement(_doc);
75             CountModels(); 
76         }
77         else 
78             throw string("Unable to read PMML file.");
79     }
80     catch ( std::string msg )
81     {
82         std::cerr << msg;
83         xmlFreeDoc(_doc);
84         xmlCleanupParser();  
85         throw;
86     }
87 }
88
89 /**
90  * Constructor to create a PMML file.
91  * @brief This constructor is mandatory for Swig because it can be used with no parameters.
92  * @param log  Flag to print logs or not
93  */
94 PMMLlib::PMMLlib(bool log):
95             _log(log),
96             _pmmlFile(""),
97             _doc(NULL),
98             _rootNode(NULL),
99             _currentNode(NULL),
100             _nbModels(0),
101             _currentModelName(""),
102             _currentModelType(kUNDEFINED)           
103 {
104     SetRootNode();
105 }
106
107 /**
108  * Destructor of the class.
109  */
110 PMMLlib::~PMMLlib()
111 {
112     if (_doc)
113         xmlFreeDoc(_doc);
114     xmlCleanupParser();
115     if ( _log )
116         cout << "~PMMLlib" << endl;
117 }
118
119 /**
120  * Set the current model and its type.
121  * @param modelName Name of the model to load (ie content of 'modelName' attribute)
122  * @param type Type of PMML to read: one of kANN or kLR
123  */
124 void PMMLlib::SetCurrentModel(std::string modelName, 
125                               PMMLType type)
126 {
127     _currentModelName = modelName;
128     _currentModelType = type;
129     switch(type)
130     {
131         case kANN:
132             _currentModelNode = GetNeuralNetPtr(modelName);
133             break;
134         case kLR:
135             _currentModelNode = GetRegressionPtr(modelName);
136             break;
137         default:
138             throw string("Unknown PMML type.");
139             break;
140     } 
141     if ( _currentModelNode == NULL )
142         throw string("Model not found.");
143 }
144
145 /**
146  * Set the current model and its type.
147  * @brief Throw an exception if there is no model or more than one model with name "modelName" in the PMML file
148  * @param modelName Name of the model to load (ie content of 'modelName' attribute)
149  */
150 void PMMLlib::SetCurrentModel(std::string modelName)
151 {
152     if (_rootNode == NULL)
153         throw string("No PMML file set.");
154     xmlNodePtr node = NULL;
155     int nC = 0;
156     node = _rootNode->children;
157     while (node)
158     {         
159         string nodeModelName = _getProp(node, string("modelName"));
160         if ( nodeModelName == modelName )
161         {
162             nC++;
163             _currentModelNode = node;
164             _currentModelName = modelName;
165             _currentModelType = GetCurrentModelType();
166         }
167         node = node->next;
168     }
169     if ( nC != 1 ) 
170     {
171         std::ostringstream oss;
172         oss << nC;
173         string msg = "SetCurrentModel(modelName) : found " + oss.str() + " model(s) in PMML file.\n";
174         msg += "Use SetCurrentModel(modelName,type).";
175         throw msg;
176     }   
177 }
178
179 /**
180  * Set the current model and its type.
181  * @brief Throw an exception if no model is found or if there are more than one model in the PMLL file
182  */
183 void PMMLlib::SetCurrentModel()
184 {
185     int nC = _nbModels;
186     if ( nC != 1 ) 
187     {
188         std::ostringstream oss;
189         oss << nC;
190         string msg = "SetCurrentModel() : found " + oss.str() + " model(s) in PMML file.\n";
191         msg += "Use SetCurrentModel(modelName) or SetCurrentModel(modelName,type).";
192         throw msg;
193     }   
194     _currentModelNode = GetChildByName(_rootNode,"NeuralNetwork"); 
195     _currentModelType = kANN;  
196     if (_currentModelNode == NULL)
197     {
198         _currentModelNode = GetChildByName(_rootNode,"RegressionModel"); 
199         _currentModelType = kLR;
200     }
201     if (_currentModelNode == NULL)
202     {
203         string msg("Couldn't get node in SetCurrentModel().");
204         throw msg;      
205     }
206     _currentModelName = _getProp(_currentModelNode, string("modelName"));
207 }
208
209 /**
210  * Make the string used by PMMLlib::printLog.
211  * @return The log
212  */
213 std::string PMMLlib::makeLog() const
214 {
215     ostringstream out;
216     out << "**\n**** Display of PMMLlib ****" << endl;
217     out << " **  _pmmlFile[" << _pmmlFile << "]" << endl;
218     out << " **  _log[" << (_log?1:0) << "]" << endl;
219     out << "**\n**** End of display of PMMLlib ****" << endl;
220     return out.str(); 
221 }
222
223 /**
224  * Print some information about the current PMML object.
225  */
226 void PMMLlib::printLog() const
227 {
228     string log = makeLog();
229     cout << log << endl;
230 }
231
232 /**
233  * Set the root node in the tree:
234  * @code
235  * <PMML version="4.1" xmlns="http://www.dmg.org/PMML-4_1">
236  * @endcode
237  */
238 void PMMLlib::SetRootNode()
239
240     xmlChar * xs = _stringToXmlChar("1.0");
241     _doc = xmlNewDoc(xs);
242     xmlFree(xs);
243     
244     xmlChar *xp = _stringToXmlChar("PMML");
245     _rootNode = xmlNewNode(0, xp);
246     xmlFree(xp);
247     
248     xmlNewProp(_rootNode, (const xmlChar*)"xmlns", (const xmlChar*)"http://www.dmg.org/PMML-4_1");
249     xmlNewProp(_rootNode, (const xmlChar*)"version", (const xmlChar*)"4.1");
250     
251     xmlDocSetRootElement(_doc, _rootNode);
252 }
253
254
255 /**
256  * Set the header node in the tree.
257  * @param copyright Copyright of the PMML file
258  * @param description Description of the model
259  * @param appName Name of the application that produced the file
260  * @param appVersion Version of the application that produced the file
261  * @param annotation Some annotation
262  */
263 void PMMLlib::SetHeader(std::string copyright, 
264                         std::string description, 
265                         std::string appName, 
266                         std::string appVersion, 
267                         std::string annotation)
268 {
269     xmlNodePtr headerNode = xmlNewChild(_rootNode, 0, (const xmlChar*)"Header", 0);
270     xmlNewProp(headerNode, (const xmlChar*)"copyright", (const xmlChar*)(copyright.c_str()));
271     xmlNewProp(headerNode, (const xmlChar*)"description", (const xmlChar*)(description.c_str()));
272
273     xmlNodePtr appNode = xmlNewChild(headerNode, 0, (const xmlChar*)"Application", 0);
274     xmlNewProp(appNode, (const xmlChar*)"name", (const xmlChar*)(appName.c_str()));
275     xmlNewProp(appNode, (const xmlChar*)"version", (const xmlChar*)(appVersion.c_str()));
276
277     xmlNewChild(headerNode, 0, (const xmlChar*)"Annotation", (const xmlChar*)(annotation.c_str()));
278 }
279
280 /**
281  * Add the MiningSchema node. 
282  * @brief Common to all models.
283  * @param name Value of property "name".
284  * @param usageType Value of property "usageType".
285  */
286 void PMMLlib::AddMiningSchema(std::string name, 
287                               std::string usageType)
288 {
289     xmlNodePtr netNode = _currentModelNode; 
290
291     // if 'MiningSchema' node does not exist, create it
292     xmlNodePtr miningSchemaNode = GetChildByName(netNode, "MiningSchema");
293     if(!miningSchemaNode)
294     {
295         miningSchemaNode = xmlNewChild(netNode, 0, (const xmlChar*)"MiningSchema", 0);
296     }
297
298     // then append the node
299     xmlNodePtr miningFieldNode = xmlNewChild(miningSchemaNode, 0, (const xmlChar*)"MiningField", 0);
300     xmlNewProp(miningFieldNode, (const xmlChar*)"name", (const xmlChar*)(name.c_str()) );
301     xmlNewProp(miningFieldNode, (const xmlChar*)"usageType", (const xmlChar*)(usageType.c_str()) );
302 }
303
304 /**
305  * Get the child of a node from the name of this node
306  * @param node Start node for the research
307  * @param nodeName Name of the node to find
308  * @return Pointer to the node found
309  */
310 xmlNodePtr PMMLlib::GetChildByName(xmlNodePtr node, 
311                                    std::string nodeName)
312 {
313     if ( node == NULL )
314         return node;
315     
316     xmlNodePtr childNode = node->children;
317     if ( childNode == NULL )   
318         return childNode;
319     
320     const xmlChar* name = childNode->name;
321     string strName("");  
322     if ( name != NULL )
323         strName =  _xmlCharToString(name);        
324     
325     while( (childNode != NULL) && (strName != nodeName) )
326     {
327       childNode = childNode->next;
328       if ( childNode == NULL )
329           return childNode;
330       name = childNode->name; 
331       if ( name != NULL )
332           strName =  _xmlCharToString(name);        
333     }
334     return childNode;
335 }
336
337 /**
338  * Count the tags of all types of models (NeuralNetwork and RegressionModel).
339  * @return Number of models
340  */
341 void PMMLlib::CountModels()
342 {
343     int nCount = 0;
344     nCount = CountNeuralNetModels() + CountRegressionModels();
345     if ( _log)
346         cout << " ** End Of Count Models nCount[" << nCount << "]" << endl;
347     _nbModels = nCount ; 
348 }
349
350 /**
351  * Count NeuralNetwork models tags in the PMML file.
352  * @return Number of models
353  */
354 int PMMLlib::CountNeuralNetModels()
355 {
356     int nCount = 0;
357     xmlNodePtr ptr = GetChildByName(_rootNode,"NeuralNetwork");  
358     // Count the models
359     while (ptr != NULL && _xmlCharToString(ptr->name) == "NeuralNetwork")
360     {
361         nCount++;
362         if (_log)
363             cout << " ** nCount[" << nCount << "]" << endl;
364         ptr = ptr->next;
365     }   
366     if ( _log)
367         cout << " ** End Of CountNetworks nCount[" << nCount << "]" << endl;    
368     return nCount;
369 }
370
371 /**
372  * Count RegressionModel models tags in the PMML file.
373  * @return Number of models
374  */
375 int PMMLlib::CountRegressionModels()
376 {
377     int nCount = 0;
378     xmlNodePtr ptr = GetChildByName(_rootNode,"RegressionModel");  
379     // Count the models
380     while (ptr != NULL && _xmlCharToString(ptr->name) == "RegressionModel")
381     {
382         nCount++;
383         if (_log)
384             cout << " ** nCount[" << nCount << "]" << endl;
385         ptr = ptr->next;
386     }   
387     if ( _log)
388         cout << " ** End Of CountRegressions nCount[" << nCount << "]" << endl;    
389     return nCount;
390 }
391
392 /**
393  * Get the number of models
394  * @return Number of models
395  */
396 int PMMLlib::GetModelsNb()
397 {
398     return _nbModels;
399 }
400
401 /**
402  * Get the name of the XML node of a given model
403  * @param node Model node
404  * @return value of attribute "modelName" of the model node
405  */
406 std::string PMMLlib::GetModelName(xmlNodePtr node)
407 {
408     string name("");
409     name = _getProp(node, string("modelName") );
410     return name;
411 }
412
413 /**
414  * Get a pointer to the index-th node named name
415  * @param index Index of the node to search
416  * @param name Name of the node
417  * @return Pointer to the node found
418  */
419 xmlNodePtr PMMLlib::GetPtr(int index, 
420                            std::string name)
421 {
422     xmlNodePtr node = NULL;
423
424     if (_doc != NULL)
425     {
426         _rootNode = xmlDocGetRootElement(_doc);
427         node = GetChildByName(_rootNode, name);
428
429         int i=0;
430         while ((i != index) && (node != NULL))
431         {
432             node = node->next;
433             i++;
434         }
435     }
436     return node;
437 }
438
439 /**
440  * Get a pointer to the node named name whose 'modelName' attribute is ann_name
441  * @param modelName Model name of the node to search
442  * @param nodeName Name of the node
443  * @return Pointer to the node found
444  */
445 xmlNodePtr PMMLlib::GetPtr(std::string myModelName, 
446                            std::string nodeName)
447 {
448     xmlNodePtr node = NULL;
449     if (_doc != NULL)
450     {
451         node = GetChildByName(_rootNode, nodeName);
452         if( node )
453         {
454             string modelName = _getProp(node, string("modelName"));
455
456             while ( (node != NULL) && modelName != myModelName )
457             {
458                 node = node->next;
459                 if( node )
460                 {
461                     modelName = _getProp(node, string("modelName"));
462                 }
463             }
464         }
465     }
466     return node;
467 }
468
469 /**
470  * Get the tag of the current model.
471  * @return Current model tag
472  */
473 std::string PMMLlib::GetTypeString()
474 {
475     string name = "";
476     switch(_currentModelType)
477     {
478     case kANN:
479         name = "NeuralNetwork";
480         break;
481     case kLR:
482         name = "RegressionModel";
483         break;
484     default:
485         throw string("Unknown PMML type.");
486         break;
487     }
488     return name;
489 }
490
491 /**
492  * Get the current model type.
493  * @brief type is kUNDEFINED if no model is set or if model type is not handled
494  * @return the type
495  */
496 PMMLType PMMLlib::GetCurrentModelType()
497 {
498     PMMLType type = kUNDEFINED ; 
499     if ( ! _currentModelNode )
500         return type;
501     string name = _xmlCharToString(_currentModelNode->name);
502     if ( name == "NeuralNetwork" )
503         type = kANN;
504     else if ( name == "RegressionModel" )
505         type = kLR;
506     return type;
507 }
508
509 /**
510  * Get the current model name.
511  * @brief name is "" if no model is set 
512  * @return the type
513  */
514 std::string PMMLlib::GetCurrentModelName()
515 {
516     if ( ! _currentModelNode )
517         return string("");
518     string name = _getProp(_currentModelNode, string("modelName"));
519     return name;  
520 }
521
522 /**
523  * Unlink the current model node.
524  */
525 void PMMLlib::UnlinkNode()
526 {
527     xmlNodePtr ptr = _currentModelNode ;
528     xmlUnlinkNode( ptr );
529     xmlFreeNode( ptr );
530 }
531
532 /**
533  * Make a backup of the current model node.
534  */
535 void PMMLlib::BackupNode()
536 {
537     // Node name depending of PMML type
538     string name = GetTypeString();
539     // Find the last save index number
540     int nCrtIndex = 0;
541     stringstream ss;
542     ss << _currentModelName << "_" << nCrtIndex;
543     xmlNodePtr ptr = GetPtr(ss.str(), name);
544     while( ptr )
545     {
546         nCrtIndex++;
547         if (_log)
548             cout << " ** nCrtIndex[" << nCrtIndex << "]" << endl;
549
550         ss.str("");
551         ss << _currentModelName << "_" << nCrtIndex;
552         ptr = GetPtr(ss.str(), name);
553     }
554     if(_log)
555         cout << " *** Node \"" << _currentModelName << "\" found, then backup it with index [" << nCrtIndex << "]" << endl;
556     // Rename model
557     xmlUnsetProp(_currentModelNode, (const xmlChar*)"modelName");
558     xmlNewProp(_currentModelNode, (const xmlChar*)"modelName", (const xmlChar*)(ss.str().c_str()));
559 }
560
561 /**
562  * Save the XML tree in the PMML file
563  */
564 void PMMLlib::Write()
565 {
566     // Enregistrement de l'arbre DOM dans le fichier pmml
567     Write(_pmmlFile);
568     // Mise à jour du nombre de modèles
569     CountModels();
570 }
571
572 /**
573  * Save the XML tree in a given file
574  * @param Name of the file  
575  */
576 void PMMLlib::Write(std::string file)
577 {
578     // Enregistrement de l'arbre DOM sous forme de fichier pmml
579     int ret = xmlSaveFormatFile( file.c_str(), _doc, 1);
580     if ( ret == -1 )
581     {
582         std::string msg("  *** Error :: unable to write the PMML file \"" + file + "\"") ; 
583         cout << msg << endl;
584         throw msg;
585     } 
586     if ( _log )
587         cout << "  *** Write the PMML file \"" << file <<"\"" << endl;
588 }
589
590 /**
591  * Export the current model as a function in a Cpp file.
592  * @param file Name of the file  
593  * @param functionName Name of the function  
594  * @param header Header of the function  
595  */
596 void PMMLlib::ExportCpp(std::string file, 
597                         std::string functionName, 
598                         std::string header)
599 {
600     if ( _currentModelType == kANN )
601         ExportNeuralNetworkCpp(file,functionName, header);
602     else if ( _currentModelType == kLR )
603     {
604         ExportLinearRegressionCpp(file, functionName, header);
605     }
606     else 
607         throw string("ExportCpp : PMML type not handled.");
608 }
609
610 /**
611  * Export the current model as a function in a Fortran file.
612  * @param file Name of the file  
613  * @param functionName Name of the function  
614  * @param header Header of the function  
615  */
616 void PMMLlib::ExportFortran(std::string file, 
617                             std::string functionName, 
618                             std::string header)
619 {
620     if ( _currentModelType == kANN )
621         ExportNeuralNetworkFortran(file,functionName, header);
622     else if ( _currentModelType == kLR )
623         ExportLinearRegressionFortran(file,functionName, header);
624     else 
625         throw string("ExportFortran : PMML type not handled.");  
626 }
627
628 /**
629  * Export the current model as a function in a Python file.
630  * @param file Name of the file  
631  * @param functionName Name of the function  
632  * @param header Header of the function  
633  */
634 void PMMLlib::ExportPython(std::string file, 
635                            std::string functionName, 
636                            std::string header)
637 {
638     if ( _currentModelType == kANN )
639         ExportNeuralNetworkPython(file,functionName, header);
640     else if ( _currentModelType == kLR )
641         ExportLinearRegressionPython(file,functionName, header);
642     else 
643         throw string("ExportPython : PMML type not handled.");    
644 }
645
646 /**
647  * Export the current model as a function in a Python string.
648  * @param file Name of the file  
649  * @param functionName Name of the function  
650  * @param header Header of the function  
651  * @return Function as a string
652  */
653 std::string PMMLlib::ExportPyStr(std::string functionName, 
654                                  std::string header)
655 {
656     if ( _currentModelType == kANN )
657         return ExportNeuralNetworkPyStr(functionName, header);
658     else if ( _currentModelType == kLR )
659         return ExportLinearRegressionPyStr(functionName, header);
660     else 
661         throw string("ExportPyStr : PMML type not handled.");    
662 }
663
664 /*!
665  * Conversion from a libxml2 string (xmlChar *) to a standard C++ string.
666  *    \param xs a constant libxml string.
667  *    \return a C++ std::string (contains the same text as xs).
668  */
669 std::string PMMLlib::_xmlCharToString(const xmlChar *xs) const
670 {
671     size_t i, L = xmlStrlen(xs);
672     std::string s;
673     s.resize(L);
674     for (i=0; *xs; s[i++] = *xs++);
675     return s;
676 }
677
678 /*!
679  * Conversion from a a standard C++ string to a libxml2 string (xmlChar *).
680  *    \param s Constant C++ std::string (contains the same text as xs)
681  *    \return Constant libxml string.
682  */
683 xmlChar * PMMLlib::_stringToXmlChar(const std::string &s) const
684 {
685     return xmlCharStrdup(s.c_str());
686 }
687
688 /*!
689  * Get the value of a node property.
690  *    \param node Tag
691  *    \param prop Property
692  *    \return Constant libxml string.
693  */
694 std::string PMMLlib::_getProp(const xmlNodePtr node, 
695                               std::string const & prop ) const
696 {
697     std::string name("");
698     if (_doc != NULL)
699     {
700         xmlChar *xp = _stringToXmlChar(prop);
701         xmlChar * attr ;
702         attr = xmlGetProp(node, xp );
703         if ( attr ) 
704         {
705             name = _xmlCharToString(attr );
706             xmlFree(attr);   
707         }  
708         xmlFree(xp);
709     }
710     return name;
711 }
712
713 //**************************************************************
714 //                                                             *
715 //                                                             *
716 //                                                             *
717 //  méthodes propres au NeuralNetwork                          *
718 //                                                             *
719 //                                                             *
720 //                                                             *
721 //**************************************************************
722
723 /*!
724  * Check if the current model type is kANN.
725  *    \brief Called in all methods specific to the NeuralNetwork model.
726  *    \brief Throw an exception if the model type is not kANN.
727  */
728 void PMMLlib::CheckNeuralNetwork()
729 {
730     if ( _currentModelType != kANN )
731         throw string("Use this method with NeuralNetwork models.");
732 }
733
734 /**
735  * Get the XML node of a given network from the index
736  * @param index Index of the neural network
737  * @return Pointer to the XML node
738  */
739 xmlNodePtr PMMLlib::GetNeuralNetPtr(int index)
740 {  
741     return GetPtr(index, GetTypeString() );
742 }
743
744 /**
745  * Get the XML node of a given network model
746  * @param name Name of the neural network
747  * @return Pointer to the XML node
748  */
749 xmlNodePtr PMMLlib::GetNeuralNetPtr(std::string name)
750 {   
751     return GetPtr(name, GetTypeString() );
752 }
753
754 /**
755  * Read the structure of the network 
756  * @brief Specific to NeuralNetwork 
757  * @return Structure read
758  */
759 std::string PMMLlib::ReadNetworkStructure()
760 {   
761     CheckNeuralNetwork(); 
762     
763     string structure("");
764     // Treatment of the input
765     xmlNodePtr inputNodes = GetChildByName(_currentModelNode,"NeuralInputs");
766     if ( inputNodes != NULL )
767     {
768         xmlNodePtr inputNode = GetChildByName(inputNodes,"NeuralInput");
769         if ( inputNode != NULL ) 
770         {
771             while (inputNode != NULL)
772             {
773                 xmlNodePtr child = GetChildByName(inputNode,"DerivedField");
774                 if ( child != NULL )
775                 {
776                     xmlNodePtr fieldName = child->children; // NormContinuous
777                     if ( fieldName != NULL )
778                     {
779                         string field = _getProp(fieldName, string("field"));
780                         structure += field;
781                         structure += ":";
782                     }
783                 } 
784                 inputNode = inputNode->next;
785             }
786             // Delete the last comma
787             structure.erase(structure.size()-1);
788         }
789     }
790     // Intermediary layers
791     xmlNodePtr node_layer = GetChildByName(_currentModelNode,"NeuralLayer");
792     if ( node_layer != NULL )
793     {
794         string name = string((const char*)(node_layer->name));
795         structure += ",";
796
797         while ( node_layer != NULL &&
798                 (string((const char*)(node_layer->name)) == "NeuralLayer") &&
799                 node_layer->next != NULL &&
800                 (string((const char*)(node_layer->next->name)) != "NeuralOutputs") )
801         {
802             // Get the number of neurons of the current layer
803             string nbneurons = _getProp(node_layer, string("numberOfNeurons"));
804             structure += nbneurons;
805             structure += ",";
806             node_layer = node_layer->next;
807         }
808     }
809     // Output layers
810     xmlNodePtr node_outputs = GetChildByName(_currentModelNode,"NeuralOutputs");
811     if ( node_outputs != NULL )
812     {    
813         xmlNodePtr node_output = GetChildByName(node_outputs,"NeuralOutput");
814         if ( node_output != NULL )
815         {   
816             while (node_output != NULL)
817             {
818                 // Get the input of the current layer
819                 xmlNodePtr child = GetChildByName(node_output,"DerivedField");
820                 if ( child != NULL )
821                 {                
822                     xmlNodePtr fieldName = child->children; // NormContinuous
823                     if ( fieldName != NULL )
824                     {
825                         if (string((const char*)(fieldName->name)) == "NormContinuous")
826                             structure += "@";
827
828                         string field = _getProp(fieldName, string("field"));
829                         structure += field;
830                         structure += ":"; 
831                     }    
832                 }
833                 node_output = node_output->next;
834             }
835             // Delete the last comma
836             structure.erase(structure.size()-1);
837         }
838     }
839     return structure;
840 }
841
842 /**
843  * Get the number of inputs, ie the number of NeuralInputs nodes.
844  * @brief Specific to NeuralNetwork 
845  * @return Number of input nodes
846  */
847 int PMMLlib::GetNbInputs()
848 {
849     CheckNeuralNetwork();  
850     
851     int nb=0;
852     xmlNodePtr node_inputs = GetChildByName(_currentModelNode,"NeuralInputs");
853     if ( node_inputs == NULL )
854       return nb;
855     
856     node_inputs = node_inputs->children;
857     while (node_inputs != NULL)
858     {
859         nb++;
860         node_inputs = node_inputs->next;
861     }
862
863     return nb;
864 }
865
866 /**
867  * Recover the number of outputs
868  * @brief Specific to NeuralNetwork 
869  * @return Number of outputs
870  */
871 int PMMLlib::GetNbOutputs()
872 {
873     CheckNeuralNetwork();   
874     
875     int nb=0;
876     xmlNodePtr node_outputs = GetChildByName(_currentModelNode,"NeuralOutputs");
877     if ( node_outputs == NULL )
878       return nb;    
879     
880     node_outputs = node_outputs->children;
881
882     while (node_outputs != NULL)
883     {
884         nb++;
885         node_outputs = node_outputs->next;
886     }
887
888     return nb;
889 }
890
891 /**
892  * Recovery of the name of an input in the current model.
893  * @brief Specific to NeuralNetwork 
894  * @param index Index of the input
895  * @return Name of the input
896  */
897 std::string PMMLlib::GetNameInput(int index)
898 {
899     CheckNeuralNetwork();  
900     
901     string name("");
902     xmlNodePtr node_inputs = GetChildByName(_currentModelNode,"NeuralInputs");
903     if ( node_inputs == NULL )
904         return name;   
905     
906     node_inputs = node_inputs->children;
907     if ( node_inputs == NULL )
908         return name; 
909     
910     for(int i = 0;i<index;i++)
911     {
912         node_inputs = node_inputs->next;
913         if ( node_inputs == NULL )
914             return name;         
915     }
916
917     node_inputs = node_inputs->children;
918     if ( node_inputs == NULL )
919         return name; 
920     
921     node_inputs = node_inputs->children;
922     if ( node_inputs == NULL )
923         return name;     
924
925     name = _getProp(node_inputs, string("field"));
926     
927     return name;
928 }
929
930 /**
931  * Get the name of an output in the current model.
932  * @brief Specific to NeuralNetwork 
933  * @param index Index of the output
934  * @return Name of the output
935  */
936 std::string PMMLlib::GetNameOutput(int index)
937 {
938     CheckNeuralNetwork();  
939     
940     string name("");
941     xmlNodePtr node_outputs = GetChildByName(_currentModelNode,"NeuralOutputs");
942     if ( node_outputs == NULL )
943       return name;       
944     node_outputs = node_outputs->children;
945     if ( node_outputs == NULL )
946       return name;   
947     for(int i = 0;i<index;i++)
948     {
949         node_outputs = node_outputs->next;
950         if ( node_outputs == NULL )
951           return name;          
952     }
953
954     node_outputs = node_outputs->children;
955     if ( node_outputs == NULL )
956       return name;  
957     node_outputs = node_outputs->children;
958     if ( node_outputs == NULL )
959       return name;      
960
961     name = _getProp(node_outputs, string("field") );    
962
963     return name;
964 }
965
966 /**
967  * Get the normalization type of the current model
968  * @brief Specific to NeuralNetwork
969  * @return Normalization type of the neural network
970  */
971 int PMMLlib::GetNormalizationType()
972 {
973     CheckNeuralNetwork();  
974     
975     xmlNodePtr node_inputs = GetChildByName(_currentModelNode,"NeuralInputs");
976     node_inputs = GetChildByName(node_inputs,"NeuralInput");
977     xmlNodePtr nodeTmp = GetChildByName(node_inputs,"DerivedField");
978     xmlNodePtr node_field = nodeTmp->children;
979     xmlNodePtr node_linearnorm;
980     string str_tmp;
981     double dorig1, dnorm1;
982     double dorig2, dnorm2;
983     if (string((const char*)(node_field->name)) == "NormContinuous")
984     {
985         // Get mean and standard deviation
986         node_linearnorm = node_field->children;
987         str_tmp = _getProp(node_linearnorm, string("orig"));
988         dorig1 = atof(str_tmp.c_str());
989         str_tmp = _getProp(node_linearnorm, string("norm"));
990         dnorm1 = atof(str_tmp.c_str());
991         node_linearnorm = node_linearnorm->next;
992         str_tmp = _getProp(node_linearnorm, string("orig"));
993         dorig2 = atof(str_tmp.c_str());
994         str_tmp = _getProp(node_linearnorm, string("norm"));
995         dnorm2 = atof(str_tmp.c_str());
996         if ( dnorm1 * dnorm2  < -0.5 )
997         {   // case of kMinusOneOne
998             return 0;
999         }
1000         else
1001         {   // case of kCR, kZeroOne
1002             return 1;
1003         }
1004     }
1005     string msg("Unable to retrieve the normalization type.");
1006     throw msg;
1007 }
1008
1009 /**
1010  * Get the input parameters on the normalization
1011  * @brief Specific to NeuralNetwork 
1012  * @param node_ann Neural network node
1013  * @param index Index of the input
1014  * @param[out] dnorm Array that contains the mean and the standard deviation
1015  */
1016 void PMMLlib::GetNormalisationInput(int index, 
1017                                     double *dnorm)
1018 {
1019     CheckNeuralNetwork();     
1020     dnorm[0] = 0.0;
1021     dnorm[1] = 0.0;
1022     xmlNodePtr node_inputs = GetChildByName(_currentModelNode,"NeuralInputs");
1023     if ( node_inputs == NULL )
1024         return ;       
1025     node_inputs = GetChildByName(node_inputs,"NeuralInput");
1026     if ( node_inputs == NULL )
1027         return ;    
1028     // Positionnement sur la bonne entree
1029     for(int i=0;i<index;i++)
1030     {
1031         node_inputs = node_inputs->next;
1032         if ( node_inputs == NULL )
1033             return ;  
1034     }
1035     xmlNodePtr tmpNode = GetChildByName(node_inputs,"DerivedField");
1036     if ( tmpNode == NULL )
1037         return ; 
1038     xmlNodePtr node_field = GetChildByName(tmpNode,"NormContinuous");
1039     if ( node_field == NULL )
1040         return ; 
1041     if (string((const char*)(node_field->name)) == "NormContinuous")
1042     {
1043         //Get mean and standard deviation
1044         string str_tmp;        
1045         xmlNodePtr node_linearnorm = node_field->children;
1046         str_tmp = _getProp(node_linearnorm, string("orig"));
1047         double dorig1 = atof(str_tmp.c_str());
1048         str_tmp = _getProp(node_linearnorm, string("norm"));
1049         double dnorm1 = atof(str_tmp.c_str());
1050         node_linearnorm = node_linearnorm->next;
1051         str_tmp = _getProp(node_linearnorm, string("orig"));
1052         double dorig2 = atof(str_tmp.c_str());
1053         str_tmp = _getProp(node_linearnorm, string("norm"));
1054         double dnorm2 = atof(str_tmp.c_str());
1055         if ( dnorm1 * dnorm2  < -0.5 ) // <=> GetNormalizationType == 0
1056         {
1057             // case of kMinusOneOne
1058             dnorm[0] = dorig1;
1059             dnorm[1] = dorig2;
1060         } 
1061         else // <=> GetNormalizationType == 1
1062         {
1063             // case of kCR, kZeroOne
1064             dnorm[0] = dorig2;
1065             dnorm[1] = -1.0 * dnorm1 * dorig2; //dorig2 / dnorm1; 
1066         }
1067     }
1068 }
1069
1070 /**
1071  * Get the parameters on the normalization of an output for the current model.
1072  * @brief Specific to NeuralNetwork 
1073  * @param index Output index
1074  * @param[out] dnorm Array that contains the mean and the standard deviation
1075  */
1076 void PMMLlib::GetNormalisationOutput(int index, 
1077                                      double *dnorm)
1078 {
1079     CheckNeuralNetwork();     
1080     dnorm[0] = 0.0;
1081     dnorm[1] = 0.0;
1082     
1083     xmlNodePtr node_outputs = GetChildByName(_currentModelNode,"NeuralOutputs");
1084     if ( node_outputs == NULL )
1085         return ;      
1086     node_outputs = GetChildByName(node_outputs,"NeuralOutput");
1087     if ( node_outputs == NULL )
1088         return ;   
1089     // Positionnement sur la bonne sortie
1090     for(int i=0;i< index;i++)
1091     {
1092         node_outputs = node_outputs->next;
1093         if ( node_outputs == NULL )
1094             return ;        
1095     }
1096     xmlNodePtr tmpNode = GetChildByName(node_outputs,"DerivedField");
1097     if ( tmpNode == NULL )
1098         return ;   
1099     xmlNodePtr node_field = GetChildByName(tmpNode,"NormContinuous"); 
1100     if ( node_field == NULL )
1101         return ;    
1102
1103     if (string((const char*)(node_field->name)) == "NormContinuous")
1104     {
1105         // Recuperation de la moyenne et de l'ecart type
1106         string str_tmp;
1107         xmlNodePtr node_linearnorm = node_field->children;
1108         str_tmp = _getProp(node_linearnorm, string("orig"));
1109         double dorig1 = atof(str_tmp.c_str());
1110         str_tmp = _getProp(node_linearnorm, string("norm"));
1111         double dnorm1 = atof(str_tmp.c_str());
1112         node_linearnorm = node_linearnorm->next;
1113         str_tmp = _getProp(node_linearnorm,string("orig"));
1114         double dorig2 = atof(str_tmp.c_str());
1115         str_tmp = _getProp(node_linearnorm, string("norm"));
1116         double dnorm2 = atof(str_tmp.c_str());
1117         if ( dnorm1 * dnorm2  < -0.5 ) 
1118         {
1119             // case of kMinusOneOne
1120             dnorm[0] = dorig1;
1121             dnorm[1] = dorig2;
1122         } 
1123         else 
1124         {
1125             // case of kCR, kZeroOne
1126             dnorm[0] = dorig2;
1127             dnorm[1] = -1.0 * dorig2 * dnorm1; //-1.0 * dorig2 / dnorm1;
1128         }
1129     }
1130 }
1131
1132 /**
1133  * Get the number of hidden layers
1134  * @brief Specific to NeuralNetwork 
1135  * @return Number of hidden layers
1136  */
1137 int PMMLlib::GetNbHiddenLayers()
1138 {
1139     CheckNeuralNetwork();
1140     
1141     int nb_layers = 0;  
1142     xmlNodePtr node_layers = GetChildByName(_currentModelNode,"NeuralLayer");
1143     if ( node_layers == NULL )
1144       return nb_layers;
1145     
1146     while (string((const char*)(node_layers->name)) == "NeuralLayer")
1147     {
1148         nb_layers++;
1149         node_layers = node_layers->next;
1150         if ( node_layers == NULL )
1151           return nb_layers;    
1152     }
1153     return nb_layers;
1154 }
1155
1156 /**
1157  * Get the total number of layers
1158  * @return Total number of layers
1159  */
1160 int PMMLlib::GetNbLayers()
1161 {
1162     return (GetNbHiddenLayers() + 2);
1163 }
1164
1165 /**
1166  * Get the number of neurons at a given layer
1167  * @param index Index of the layer
1168  * @return Number of neurons at given layer
1169  */
1170 int PMMLlib::GetNbNeuronsAtLayer(int index)
1171 {
1172     CheckNeuralNetwork();  
1173     
1174     int nb_neurons = 0;
1175     xmlNodePtr node_layers = GetChildByName(_currentModelNode,"NeuralLayer");
1176     if ( node_layers == NULL )
1177         return nb_neurons;  
1178
1179     // Positionnement à la bonne couche
1180     for(int i=0;i<index;i++)
1181     {
1182         node_layers = node_layers->next;
1183         if ( node_layers == NULL )
1184             return nb_neurons;         
1185     }
1186
1187     xmlNodePtr node_neurons = GetChildByName(node_layers,"Neuron");
1188     while(node_neurons != NULL)
1189     {
1190         nb_neurons++;
1191         node_neurons = node_neurons->next;
1192     }
1193
1194     return nb_neurons;
1195 }
1196
1197 /**
1198  * Get the bias of a neuron
1199  * @brief Specific to NeuralNetwork 
1200  * @param layer_index Index of the layer to get bias
1201  * @param neu_index Index of the neuron
1202  * @return Bias of the specified neuron
1203  */
1204 double PMMLlib::GetNeuronBias(int layer_index, 
1205                               int neu_index)
1206 {
1207     CheckNeuralNetwork();  
1208     
1209     double bias = 0.;
1210     xmlNodePtr node_layers = GetChildByName(_currentModelNode,"NeuralLayer");
1211     if ( node_layers == NULL )
1212         return bias;  
1213     // Positionnement a la bonne couche
1214     for(int i=0;i<layer_index;i++)
1215     {
1216         node_layers = node_layers->next;
1217         if ( node_layers == NULL )
1218             return bias;          
1219     }
1220     xmlNodePtr node_neurons = GetChildByName(node_layers,"Neuron");
1221     // Positionnement sur le bon neurone
1222     for(int j=0;j<neu_index;j++)
1223     {
1224         node_neurons = node_neurons->next;
1225         if ( node_neurons == NULL )
1226             return bias;           
1227     }
1228     string str_tmp  = _getProp(node_neurons, string("bias"));
1229     bias = atof(str_tmp.c_str());
1230     return bias;
1231 }
1232
1233 /**
1234  * Get the synaptic weight
1235  * @brief Specific to NeuralNetwork 
1236  * @param layer_index Index of the layer to get synaptic weight
1237  * @param neu_index Index of the neuron
1238  * @param prec_index Index of the synapse
1239  * @return Synaptic weight
1240  */
1241 double PMMLlib::GetPrecNeuronSynapse(int layer_index, 
1242                                      int neu_index, 
1243                                      int prec_index)
1244 {
1245     CheckNeuralNetwork();
1246     
1247     double weight = 0.;
1248     xmlNodePtr node_layers = GetChildByName(_currentModelNode,"NeuralLayer");
1249     if ( node_layers == NULL )
1250         return weight;     
1251     // Positionnement a la bonne couche
1252     for(int i=0;i<layer_index;i++)
1253     {
1254         node_layers = node_layers->next;
1255         if ( node_layers == NULL )
1256             return weight;        
1257     }
1258     xmlNodePtr node_neurons = GetChildByName(node_layers,"Neuron");
1259     // Positionnement sur le bon neurone
1260     for(int i=0;i<neu_index;i++)
1261     {
1262         node_neurons = node_neurons->next;
1263         if ( node_neurons == NULL )
1264             return weight;          
1265     }
1266     xmlNodePtr node_con = GetChildByName(node_neurons,"Con");
1267     // Positionnement sur la bonne synapse
1268     for(int i=0;i<prec_index;i++)
1269     {
1270         node_con = node_con->next;
1271         if ( node_con == NULL )
1272             return weight;          
1273     }
1274     string str_tmp  = _getProp(node_con, string("weight"));
1275     weight = atof(str_tmp.c_str());
1276     return weight;
1277 }
1278
1279 /**
1280  * Set the name of the neural network
1281  * @brief Not tested 
1282  * @param index Neural network index
1283  * @param name Neural network name to set
1284  */
1285 // LCOV_EXCL_START
1286 void PMMLlib::SetNeuralNetName(int index, 
1287                                std::string name)
1288 {
1289     CheckNeuralNetwork();
1290     
1291     int i=0;
1292     if (_doc != NULL)
1293     {
1294         xmlNodePtr node_ann = GetChildByName(_rootNode,"NeuralNetwork");
1295         while ((i != index) && (node_ann != NULL))
1296         {
1297             node_ann = node_ann->next;
1298             i++;
1299         }
1300         xmlNewProp(node_ann, (const xmlChar*)"modelName", (const xmlChar*)(name.c_str()));
1301     }
1302     xmlSaveFormatFile( string(_pmmlFile+".pmml").c_str(), _doc, 1);
1303 }
1304 // LCOV_EXCL_STOP
1305
1306 /**
1307  * Add a DataField node to the DataDictionnary node
1308  * @param fieldName Value of property "name"
1309  * @param displayName Value of property "displayName"
1310  * @param optype Value of property "optype"
1311  * @param dataType Value of property "dataType"
1312  * @param closure Value of property "closure" in node Interval
1313  * @param leftMargin Value of property "leftMargin" in node Interval
1314  * @param rightMargin Value of property "rightMargin" in node Interval
1315  * @param interval Flag to add a node Interval (if true)
1316  */
1317 void PMMLlib::AddDataField(std::string fieldName, 
1318                            std::string displayName, 
1319                            std::string optype,
1320                            std::string dataType, 
1321                            std::string closure, 
1322                            double leftMargin, 
1323                            double rightMargin, 
1324                            bool interval)
1325 {
1326     // if 'DataDictionary' node does not exist, create it
1327     xmlNodePtr dataDictNode = GetChildByName(_rootNode, "DataDictionary");
1328     if(!dataDictNode)
1329     {
1330         dataDictNode = xmlNewChild(_rootNode, 0, (const xmlChar*)"DataDictionary", 0);
1331     }
1332
1333     // then append the node
1334     xmlNodePtr dataFieldNode = xmlNewChild(dataDictNode, 0, (const xmlChar*)"DataField", 0);
1335     xmlNewProp(dataFieldNode, (const xmlChar*)"name", (const xmlChar*)(fieldName.c_str()) );
1336     xmlNewProp(dataFieldNode, (const xmlChar*)"displayName", (const xmlChar*)(displayName.c_str()) );
1337     xmlNewProp(dataFieldNode, (const xmlChar*)"optype", (const xmlChar*)(optype.c_str()) );
1338     xmlNewProp(dataFieldNode, (const xmlChar*)"dataType", (const xmlChar*)(dataType.c_str()) );
1339
1340     if ( interval ) 
1341     {
1342         xmlNodePtr intervalNode = xmlNewChild(dataFieldNode, 0, (const xmlChar*)"Interval", 0);
1343         xmlNewProp(intervalNode, (const xmlChar*)"closure", (const xmlChar*)(closure.c_str()) );
1344         stringstream ss;
1345         ss << scientific << leftMargin;
1346         xmlNewProp(intervalNode, (const xmlChar*)"leftMargin", (const xmlChar*)(ss.str().c_str()) );
1347         ss.str("");
1348         ss << scientific << rightMargin;
1349         xmlNewProp(intervalNode, (const xmlChar*)"rightMargin", (const xmlChar*)(ss.str().c_str()) );
1350     }
1351 }
1352
1353 /**
1354  * Add a NeuralNetwork node to the root node
1355   * @brief Specific to NeuralNetwork
1356  * @param modelName Model name
1357  * @param functionName PMMLMiningFunction. One of : kREGRESSION.
1358  */ 
1359 void PMMLlib::AddNeuralNetwork(std::string modelName, 
1360                                PMMLMiningFunction functionName)
1361 {
1362     _currentModelType = kANN;
1363     _currentModelName = modelName;
1364     
1365     CheckNeuralNetwork();
1366     
1367     string function;
1368     switch(functionName)
1369     {
1370     case kREGRESSION:
1371         function = "regression"; 
1372         break;
1373     }
1374
1375     xmlNodePtr netNode = xmlNewChild(_rootNode, 0, (const xmlChar*)"NeuralNetwork", 0);
1376     xmlNewProp(netNode, (const xmlChar*)"modelName", (const xmlChar*)(_currentModelName.c_str()) );
1377     xmlNewProp(netNode, (const xmlChar*)"functionName", (const xmlChar*)(function.c_str()) );
1378     xmlNewProp(netNode, (const xmlChar*)"numberOfLayers", (const xmlChar*)"0" );
1379     _currentModelNode = netNode;
1380 }
1381
1382  /**
1383  * Add a NeuralInput node to the current model.
1384   * @brief Specific to NeuralNetwork
1385  * @param id Id of the input
1386  * @param inputName Name of the input
1387  * @param optype Value of property "optype"
1388  * @param dataType Value of property "dataType" 
1389  * @param orig1 Value of the first origin
1390  * @param norm1 Value of the first norm
1391  * @param orig2 Value of the second origin
1392  * @param norm2 Value of the second norm
1393  */ 
1394 void PMMLlib::AddNeuralInput(int id, 
1395                              std::string inputName, 
1396                              std::string optype, 
1397                              std::string dataType, 
1398                              double orig1, double norm1, 
1399                              double orig2, double norm2)
1400 {
1401     CheckNeuralNetwork();
1402
1403     xmlNodePtr netNode = _currentModelNode; 
1404     // if 'NeuralInputs' node does not exist, create it
1405     xmlNodePtr neuralInputsNode = GetChildByName(netNode, "NeuralInputs");
1406     if(!neuralInputsNode)
1407     {
1408         neuralInputsNode = xmlNewChild(netNode, 0, (const xmlChar*)"NeuralInputs", 0);
1409         xmlNewProp(neuralInputsNode, (const xmlChar*)"numberOfInputs", (const xmlChar*)"0" );
1410     }
1411     // increment the number of inputs
1412     string numberOfInputsStr = _getProp(neuralInputsNode, string("numberOfInputs"));
1413     int numberOfInputs;
1414     istringstream( numberOfInputsStr ) >> numberOfInputs;        
1415     numberOfInputs++;
1416     stringstream ss;
1417     ss << numberOfInputs;
1418     xmlSetProp(neuralInputsNode, (const xmlChar*)"numberOfInputs", (const xmlChar*)(ss.str().c_str()) );
1419     // then append the node and its children
1420     xmlNodePtr neuralInputNode = xmlNewChild(neuralInputsNode, 0, (const xmlChar*)"NeuralInput", 0);
1421     ss.str(""); ss << id;
1422     xmlNewProp(neuralInputNode, (const xmlChar*)"id", (const xmlChar*)(ss.str().c_str()) );
1423
1424     xmlNodePtr derivedFieldNode = xmlNewChild(neuralInputNode, 0, (const xmlChar*)"DerivedField", 0);
1425     xmlNewProp(derivedFieldNode, (const xmlChar*)"optype", (const xmlChar*)(optype.c_str()) );
1426     xmlNewProp(derivedFieldNode, (const xmlChar*)"dataType", (const xmlChar*)(dataType.c_str()) );
1427
1428     xmlNodePtr normcontNode = xmlNewChild(derivedFieldNode, 0, (const xmlChar*)"NormContinuous", 0);
1429     xmlNewProp(normcontNode, (const xmlChar*)"field", (const xmlChar*)(inputName.c_str()) );
1430
1431     xmlNodePtr node_linearnorm1 = xmlNewChild(normcontNode, 0, (const xmlChar*)"LinearNorm", 0);
1432     ss.str(""); ss << scientific << orig1;
1433     xmlNewProp(node_linearnorm1, (const xmlChar*)"orig", (const xmlChar*)(ss.str().c_str()) );
1434     ss.str(""); ss << scientific << norm1;
1435     xmlNewProp(node_linearnorm1, (const xmlChar*)"norm", (const xmlChar*)(ss.str().c_str()) );
1436     xmlNodePtr node_linearnorm2 = xmlNewChild(normcontNode, 0, (const xmlChar*)"LinearNorm", 0);
1437     ss.str(""); ss << scientific << orig2;
1438     xmlNewProp(node_linearnorm2, (const xmlChar*)"orig", (const xmlChar*)(ss.str().c_str()) );
1439     ss.str(""); ss << scientific << norm2;
1440     xmlNewProp(node_linearnorm2, (const xmlChar*)"norm", (const xmlChar*)(ss.str().c_str()) );
1441 }
1442
1443  /**
1444  * Add a NeuralOutput node to the current model.
1445  * @brief Specific to NeuralNetwork
1446  * @param outputNeuron Id of the output
1447  * @param outputName Name of the output
1448  * @param optype Value of property "optype"
1449  * @param dataType Value of property "dataType" 
1450  * @param orig1 Value of the first origin
1451  * @param norm1 Value of the first norm
1452  * @param orig2 Value of the second origin
1453  * @param norm2 Value of the second norm
1454  */ 
1455 void PMMLlib::AddNeuralOutput(int outputNeuron, 
1456                               std::string outputName, 
1457                               std::string optype, 
1458                               std::string dataType, 
1459                               double orig1, double norm1, 
1460                               double orig2, double norm2)
1461 {
1462     CheckNeuralNetwork();
1463
1464      xmlNodePtr netNode = _currentModelNode; 
1465     // if 'NeuralOutputs' node does not exist, create it
1466     xmlNodePtr neuralOutputsNode = GetChildByName(netNode, "NeuralOutputs");
1467     if(!neuralOutputsNode)
1468     {
1469         neuralOutputsNode = xmlNewChild(netNode, 0, (const xmlChar*)"NeuralOutputs", 0);
1470         xmlNewProp(neuralOutputsNode, (const xmlChar*)"numberOfOutputs", (const xmlChar*)"0" );
1471     }
1472     // increment the number of inputs
1473     string numberOfOutputsStr = _getProp(neuralOutputsNode, string("numberOfOutputs"));
1474     int numberOfOutputs;
1475     istringstream( numberOfOutputsStr ) >> numberOfOutputs;       
1476     numberOfOutputs++;
1477     stringstream ss;
1478     ss << numberOfOutputs;
1479     xmlSetProp(neuralOutputsNode, (const xmlChar*)"numberOfOutputs", (const xmlChar*)(ss.str().c_str()) );
1480
1481     // then append the node and its children
1482     xmlNodePtr neuralOutputNode = xmlNewChild(neuralOutputsNode, 0, (const xmlChar*)"NeuralOutput", 0);
1483     ss.str(""); ss << outputNeuron;
1484     xmlNewProp(neuralOutputNode, (const xmlChar*)"outputNeuron", (const xmlChar*)(ss.str().c_str()) );
1485
1486     xmlNodePtr derivedFieldNode = xmlNewChild(neuralOutputNode, 0, (const xmlChar*)"DerivedField", 0);
1487     xmlNewProp(derivedFieldNode, (const xmlChar*)"optype", (const xmlChar*)(optype.c_str()) );
1488     xmlNewProp(derivedFieldNode, (const xmlChar*)"dataType", (const xmlChar*)(dataType.c_str()) );
1489
1490     xmlNodePtr normcontNode = xmlNewChild(derivedFieldNode, 0, (const xmlChar*)"NormContinuous", 0);
1491     xmlNewProp(normcontNode, (const xmlChar*)"field", (const xmlChar*)(outputName.c_str()) );
1492
1493     xmlNodePtr node_linearnorm1 = xmlNewChild(normcontNode, 0, (const xmlChar*)"LinearNorm", 0);
1494     ss.str(""); ss << scientific << orig1;
1495     xmlNewProp(node_linearnorm1, (const xmlChar*)"orig", (const xmlChar*)(ss.str().c_str()) );
1496     ss.str(""); ss << scientific << norm1;
1497     xmlNewProp(node_linearnorm1, (const xmlChar*)"norm", (const xmlChar*)(ss.str().c_str()) );
1498     xmlNodePtr node_linearnorm2 = xmlNewChild(normcontNode, 0, (const xmlChar*)"LinearNorm", 0);
1499     ss.str(""); ss << scientific << orig2;
1500     xmlNewProp(node_linearnorm2, (const xmlChar*)"orig", (const xmlChar*)(ss.str().c_str()) );
1501     ss.str(""); ss << scientific << norm2;
1502     xmlNewProp(node_linearnorm2, (const xmlChar*)"norm", (const xmlChar*)(ss.str().c_str()) );
1503 }
1504
1505  /**
1506  * Add a NeuralLayer node to the current model.
1507   * @brief Specific to NeuralNetwork
1508  * @param activationFunction Activation function. One of kIDENTITY, kTANH, kLOGISTIC.
1509  */ 
1510 void PMMLlib::AddNeuralLayer(PMMLActivationFunction activationFunction)
1511 {
1512     CheckNeuralNetwork();
1513     
1514     string functionName;
1515     switch(activationFunction)
1516     {
1517     case kIDENTITY:
1518         functionName = "identity";
1519         break;
1520     case kTANH:
1521         functionName = "tanh";
1522         break;
1523     case kLOGISTIC:
1524         functionName = "logistic";
1525         break;
1526     }
1527     xmlNodePtr netNode = _currentModelNode; 
1528     // Increment the number of layers
1529     string numberOfLayersStr = _getProp(_currentModelNode, string("numberOfLayers"));
1530     int numberOfLayers;
1531     istringstream( numberOfLayersStr ) >> numberOfLayers;       
1532     numberOfLayers++;
1533     stringstream ss;
1534     ss << numberOfLayers;
1535     xmlSetProp(netNode, (const xmlChar*)"numberOfLayers", (const xmlChar*)(ss.str().c_str()) );
1536     // Add the neural layer node
1537     xmlNodePtr neuralLayerNode = xmlNewChild(netNode, 0, (const xmlChar*)"NeuralLayer", 0);
1538     xmlNewProp(neuralLayerNode, (const xmlChar*)"activationFunction", (const xmlChar*)(functionName.c_str()) );
1539     xmlNewProp(neuralLayerNode, (const xmlChar*)"numberOfNeurons", (const xmlChar*)"0" );
1540     // Save the current layer in the _currentNode attribute
1541     _currentNode = neuralLayerNode;
1542 }
1543
1544  /**
1545  * Add a NeuralLayer node to the current model.
1546   * @brief Specific to NeuralNetwork
1547  * @param id Id of the layer
1548  * @param bias Value of property "bias"
1549  * @param conNb Number of Con nodes
1550  * @param firstFrom Value of property "from" for the first Con
1551  * @param weights Vector of weights (One per Con node)
1552  */ 
1553 void PMMLlib::AddNeuron(int id, 
1554                         double bias, 
1555                         int conNb, 
1556                         int firstFrom, 
1557                         vector<double> weights)
1558 {
1559     CheckNeuralNetwork();
1560     
1561     stringstream ss;
1562
1563     // increment the number of neurons
1564     string numberOfNeuronsStr = _getProp(_currentNode, string("numberOfNeurons"));
1565     int numberOfNeurons;
1566     istringstream( numberOfNeuronsStr ) >> numberOfNeurons;    
1567     numberOfNeurons++;
1568     ss << numberOfNeurons;
1569     xmlSetProp(_currentNode, (const xmlChar*)"numberOfNeurons", (const xmlChar*)(ss.str().c_str()) );
1570
1571     // append a neuron
1572     xmlNodePtr neuronNode = xmlNewChild(_currentNode, 0, (const xmlChar*)"Neuron", 0);
1573     ss.str(""); ss << id;
1574     xmlNewProp(neuronNode, (const xmlChar*)"id", (const xmlChar*)(ss.str().c_str()) );
1575     ss.str(""); ss << scientific << bias;
1576     xmlNewProp(neuronNode, (const xmlChar*)"bias", (const xmlChar*)(ss.str().c_str()) );
1577
1578     // append multiple 'Con' to the neuron
1579     for(int k=0 ; k<conNb ; k++)
1580     {
1581         xmlNodePtr conNode = xmlNewChild(neuronNode, 0, (const xmlChar*)"Con", 0);
1582         ss.str(""); ss << firstFrom+k;
1583         xmlNewProp(conNode, (const xmlChar*)"from", (const xmlChar*)(ss.str().c_str()) ); // !!! ce n'est pas k !!!
1584         ss.str(""); ss << scientific << weights[k];
1585         xmlNewProp(conNode, (const xmlChar*)"weight", (const xmlChar*)(ss.str().c_str()) );
1586     }
1587 }
1588
1589  /**
1590  * Fill the vectors used by the ExportXXX methods.
1591   * @brief Specific to NeuralNetwork
1592  * @param nInput
1593  * @param nOutput
1594  * @param nHidden
1595  * @param normType
1596  * @param minInput
1597  * @param maxInput
1598  * @param minOutput
1599  * @param maxOutput
1600  * @param valW
1601  */ 
1602 void PMMLlib::fillVectorsForExport(int nInput, 
1603                                    int nOutput, 
1604                                    int nHidden, 
1605                                    int normType,
1606                                    vector<double> &minInput,
1607                                    vector<double> &maxInput,
1608                                    vector<double> &minOutput,
1609                                    vector<double> &maxOutput,
1610                                    vector<double> &valW )
1611 {
1612     CheckNeuralNetwork();
1613     
1614     xmlNodePtr netNode = _currentModelNode ; 
1615     // Get the different values required
1616     // Build min/max input/output vectors
1617     for(int i=0 ; i<nInput ; i++)
1618     {
1619         xmlNodePtr node_inputs = GetChildByName(netNode,"NeuralInputs");
1620         node_inputs = node_inputs->children;
1621         for(int j = 0;j<i;j++)
1622         {
1623             node_inputs = node_inputs->next;
1624         }
1625         node_inputs = node_inputs->children; // DerivedField
1626         node_inputs = node_inputs->children; // NormContinuous
1627         node_inputs = node_inputs->children; // LinearNorm
1628         string strOrig1 = _getProp(node_inputs, string("orig") );
1629         double orig1 = atof( strOrig1.c_str() );
1630         string strNorm1 = _getProp(node_inputs, string("norm") );        
1631         double norm1 = atof( strNorm1.c_str() );
1632         node_inputs = node_inputs->next;
1633         string strOrig2 = _getProp(node_inputs, string("orig") );
1634         double orig2 = atof( strOrig2.c_str() );
1635         string strNorm2 = _getProp(node_inputs, string("norm") );    
1636         if( normType==0 )
1637         {   // kMinusOneOne
1638             minInput[i] = orig1;
1639             maxInput[i] = orig2;
1640         }
1641         else
1642         {   //  kCR, kZeroOne
1643             minInput[i] = orig2;
1644             maxInput[i] = -1.0*norm1*orig2;
1645         }
1646     }
1647     xmlNodePtr node_outputs = GetChildByName(netNode,"NeuralOutputs");
1648     node_outputs = node_outputs->children;
1649     node_outputs = node_outputs->children; // DerivedField
1650     node_outputs = node_outputs->children; // NormContinuous
1651     node_outputs = node_outputs->children; // LinearNorm  
1652     string strOrig1 = _getProp(node_outputs, string("orig") );
1653     double orig1 = atof( strOrig1.c_str() );
1654     string strNorm1 = _getProp(node_outputs, string("norm") );        
1655     double norm1 = atof( strNorm1.c_str() ); 
1656     node_outputs = node_outputs->next;
1657     string strOrig2 = _getProp(node_outputs, string("orig") );
1658     double orig2 = atof( strOrig2.c_str() );    
1659     if( normType==0 )
1660     {   // kMinusOneOne
1661         minOutput[0] = orig1;
1662         maxOutput[0] = orig2;
1663     }
1664     else
1665     {   //  kCR, kZeroOne
1666         minOutput[0] = orig2;
1667         maxOutput[0] = -1.0*norm1*orig2;
1668     }
1669     // Build weight vector
1670     for(int j=0 ; j<nHidden ; j++) // hidden layers
1671     {
1672         valW[j*(nInput+nOutput+1)+2] = GetNeuronBias( 0, j);
1673         for(int i=0 ; i<nInput ; i++)
1674         {
1675             valW[j*(nInput+nOutput+1)+3+i] = GetPrecNeuronSynapse( 0, j, i);        
1676         }
1677     }  
1678     for(int j=0 ; j<nOutput ; j++) // output layers
1679     {
1680         valW[0] = GetNeuronBias( 1, j);                
1681         for(int i=0 ; i<nHidden ; i++)
1682         {
1683             valW[i*(nInput+nOutput+1)+1] = GetPrecNeuronSynapse( 1, j, i);           
1684         }
1685     }
1686 }
1687
1688 /**
1689  * Export the current model as a NeuralNetwork function in a Cpp file.
1690  * @brief Specific to NeuralNetwork
1691  * @param file Name of the file  
1692  * @param functionName Name of the function  
1693  * @param header Header of the function  
1694  */
1695 void PMMLlib::ExportNeuralNetworkCpp(std::string file, 
1696                                      std::string functionName, 
1697                                      std::string header)
1698 {
1699     CheckNeuralNetwork();
1700     
1701     // Get the different values required
1702     int nInput = GetNbInputs();
1703     int nOutput = GetNbOutputs();
1704     int nHidden = GetNbNeuronsAtLayer(0);
1705     int nNeurons = nInput+nOutput+nHidden;
1706     int nWeights = nHidden*(nInput+nOutput+1)+nOutput;
1707     int normType = GetNormalizationType();
1708     // Build min/max input/output vectors
1709     vector<double> minInput(nInput);
1710     vector<double> maxInput(nInput);
1711     vector<double> minOutput(nOutput);
1712     vector<double> maxOutput(nOutput);
1713     vector<double> valW(nWeights);
1714     fillVectorsForExport(nInput,nOutput,nHidden,normType,minInput,maxInput,minOutput,maxOutput,valW);
1715     // Write the file
1716     ofstream sourcefile(file.c_str());
1717     // ActivationFunction
1718     if( normType==0 )
1719     {   // kMinusOneOne
1720         sourcefile << "#define ActivationFunction(sum)         ( tanh(sum) )" << endl;
1721     }
1722     else
1723     {   //  kCR, kZeroOne
1724         sourcefile << "#define ActivationFunction(sum) ( 1.0 / ( 1.0 + exp( -1.0 * sum )) )" << endl;
1725     }
1726     //
1727     sourcefile << "void " << functionName <<"(double *param, double *res)" << endl;
1728     sourcefile << "{" << endl;
1729     // header
1730     sourcefile << "  ////////////////////////////// " << endl;
1731     sourcefile << "  //" << endl;
1732     // insert comments in header
1733     header = "  // " + header;
1734     size_t pos = 0;
1735     while ((pos = header.find("\n", pos)) != std::string::npos) 
1736     {
1737         header.replace(pos, 1, "\n  //");
1738         pos += 5;
1739     }
1740     sourcefile << header << endl;
1741     sourcefile << "  //" << endl;
1742     sourcefile << "  ////////////////////////////// " << endl;
1743     sourcefile << endl;
1744     sourcefile << "  int nInput   = " <<  nInput << ";" << endl;
1745     sourcefile << "  int nOutput   = " <<  nOutput << ";" << endl;
1746     //  sourcefile << "  int nWeights = " <<  _nWeight << ";" << endl;
1747     sourcefile << "  int nHidden  = " <<  nHidden << ";" << endl;
1748     sourcefile << "  const int nNeurones  = " <<  nNeurons << ";" << endl;
1749     sourcefile << "  double " << functionName << "_act[nNeurones];" << endl;
1750     sourcefile << endl;
1751     sourcefile << "  // --- Preprocessing of the inputs and outputs" << endl;
1752     sourcefile << "  double " << functionName << "_minInput[] = {" << endl << "  ";
1753     for(int i=0 ; i<nInput ; i++)
1754     {
1755         sourcefile << minInput[i] << ", ";
1756         if( (i+1)%5==0 )
1757             sourcefile << "\n  ";
1758     }
1759     if( nInput%5 != 0 )
1760         sourcefile << endl;
1761     sourcefile << "  };" << endl;
1762     //
1763     sourcefile << "  double " << functionName << "_minOutput[] = {" << endl << "  ";
1764     sourcefile << minOutput[0] << ", ";
1765     sourcefile << "  };" << endl;
1766     //
1767     sourcefile << "  double " << functionName << "_maxInput[] = {" << endl << "  ";
1768     for(int i=0 ; i<nInput ; i++)
1769     {
1770         sourcefile << maxInput[i] << ", ";
1771         if( (i+1)%5==0 )
1772             sourcefile << "\n  ";
1773     }
1774     if( nInput%5 != 0 )
1775         sourcefile << endl;
1776     sourcefile << "  };" << endl;
1777     //
1778     sourcefile << "  double " << functionName << "_maxOutput[] = {" << endl << "  ";
1779     sourcefile << maxOutput[0] << ", ";
1780     sourcefile << "  };" << endl;
1781     // Weights vector
1782     sourcefile << endl;
1783     sourcefile << "  // --- Values of the weights" << endl;
1784     sourcefile << "  double " << functionName << "_valW[] = {" << endl << "  ";
1785     for(int i=0 ; i<nWeights ; i++)
1786     {
1787         sourcefile << valW[i] << ", ";
1788         if ( (i+1)%5 == 0 )
1789             sourcefile << endl << "  ";
1790     }
1791     sourcefile << endl << "  };"<<endl;
1792     //
1793     sourcefile << "  // --- Constants";
1794     sourcefile << endl;
1795     sourcefile << "  int indNeurone = 0;"<<endl;
1796     sourcefile << "  int CrtW;"<<endl;
1797     sourcefile << "  double sum;"<<endl;
1798
1799     // couche  entree
1800     sourcefile << endl;
1801     sourcefile << "  // --- Input Layers"<<endl;
1802     sourcefile << "  for(int i = 0; i < nInput; i++) {"<<endl;
1803     if( normType==0 )
1804     {   // kMinusOneOne
1805         sourcefile << "     " << functionName << "_act[indNeurone++] = 2.0 * ( param[i] - "
1806                    << functionName << "_minInput[i] ) / ( " << functionName << "_maxInput[i] - "
1807                    << functionName << "_minInput[i] ) - 1.0;"<<endl;
1808     }
1809     else
1810     {   //  kCR, kZeroOne
1811         sourcefile << "     " << functionName << "_act[indNeurone++] = ( param[i] - "
1812                    << functionName << "_minInput[i] ) / " << functionName << "_maxInput[i];"
1813                    << endl;
1814     }
1815     sourcefile << "  }"<<endl;
1816
1817
1818     // couche cachee
1819     sourcefile << endl;
1820     sourcefile << "  // --- Hidden Layers"<<endl;
1821     sourcefile << "  for (int member = 0; member < nHidden; member++) {"<<endl;
1822     sourcefile << "     int CrtW = member * ( nInput + 2) + 2;" << endl;
1823     sourcefile << "     sum = " << functionName << "_valW[CrtW++];" << endl;
1824     sourcefile << "     for (int source = 0; source < nInput; source++) {" << endl;
1825     sourcefile << "         sum += " << functionName << "_act[source] * " << functionName << "_valW[CrtW++];" << endl;
1826     sourcefile << "       }" << endl;
1827     sourcefile << "       " << functionName << "_act[indNeurone++] = ActivationFunction(sum);" << endl;
1828     sourcefile << "  }"<<endl;
1829     // couche sortie
1830     sourcefile << endl;
1831     sourcefile << "  // --- Output"<<endl;
1832     sourcefile << "  for (int member = 0; member < nOutput; member++) {"<<endl;
1833     sourcefile << "    sum = " << functionName << "_valW[0];"<<endl;
1834     sourcefile << "    for (int source = 0; source < nHidden; source++) {"<<endl;
1835     sourcefile << "      CrtW = source * ( nInput + 2) + 1;"<<endl;
1836     sourcefile << "      sum += " << functionName << "_act[nInput+source] * " << functionName << "_valW[CrtW];"<<endl;
1837     sourcefile << "    }"<<endl;
1838     sourcefile << "    " << functionName << "_act[indNeurone++] = sum;"<<endl;
1839     if( normType==0 )
1840     {   // kMinusOneOne
1841         sourcefile << "    res[member] = " << functionName
1842                    << "_minOutput[member] + 0.5 * ( " << functionName
1843                    << "_maxOutput[member] - " << functionName
1844                    << "_minOutput[member] ) * ( sum + 1.0);" << endl;
1845     }
1846     else
1847     {   //  kCR, kZeroOne
1848         sourcefile << "    res[member] = " << functionName
1849                    << "_minOutput[member] + " << functionName
1850                    << "_maxOutput[member] * sum;" << endl;
1851     }
1852     sourcefile << "  }"<<endl;
1853     //
1854     sourcefile << "}" << endl;
1855     sourcefile.close();
1856 }
1857
1858 /**
1859  * Export the current model as a NeuralNetwork function in a Fortran file.
1860  * @brief Specific to NeuralNetwork
1861  * @param file Name of the file  
1862  * @param functionName Name of the function  
1863  * @param header Header of the function  
1864  */
1865 void PMMLlib::ExportNeuralNetworkFortran(std::string file, 
1866                                          std::string functionName, 
1867                                          std::string header)
1868 {
1869     CheckNeuralNetwork();  
1870     
1871     // Get the different values required
1872     int nInput = GetNbInputs();
1873     int nOutput = GetNbOutputs();
1874     int nHidden = GetNbNeuronsAtLayer(0);
1875     int nWeights = nHidden*(nInput+nOutput+1)+nOutput;
1876     int normType = GetNormalizationType();
1877     // Build min/max input/output vectors
1878     vector<double> minInput(nInput);
1879     vector<double> maxInput(nInput);
1880     vector<double> minOutput(nOutput);
1881     vector<double> maxOutput(nOutput);
1882     vector<double> valW(nWeights);
1883     fillVectorsForExport(nInput,nOutput,nHidden,normType,minInput,maxInput,minOutput,maxOutput,valW);
1884     // Write the file
1885     ofstream sourcefile(file.c_str());
1886
1887     sourcefile << "      SUBROUTINE " << functionName << "(";
1888     for(int i=0 ; i<GetNbInputs() ; i++)
1889     {
1890         sourcefile << GetNameInput(i) << ",";
1891     }
1892     sourcefile << GetNameOutput(0) << ")" << endl;
1893     // header
1894     sourcefile << "C --- *********************************************" << endl;
1895     sourcefile << "C --- " << endl;
1896     // insert comments in header
1897     header = "C ---  " + header;
1898     size_t pos = 0;
1899     while ((pos = header.find("\n", pos)) != std::string::npos) 
1900     {
1901         header.replace(pos, 1, "\nC --- ");
1902         pos += 5;
1903     }
1904     sourcefile << header << endl;
1905     sourcefile << "C --- " << endl;
1906     sourcefile << "C --- *********************************************" << endl;
1907
1908     sourcefile << "      IMPLICIT DOUBLE PRECISION (V)" << endl;
1909     for(int i=0 ; i<GetNbInputs() ; i++)
1910     {
1911         sourcefile << "      DOUBLE PRECISION " << GetNameInput(i) << endl;
1912     }
1913     sourcefile << "      DOUBLE PRECISION " << GetNameOutput(0) << endl;
1914     sourcefile << endl;
1915
1916     sourcefile << "C --- Preprocessing of the inputs" << endl;
1917     for(int i=0 ; i<GetNbInputs() ; i++)
1918     {
1919         sourcefile << "      VXN" << GetNameInput(i) << " = ";
1920
1921         if( normType==0 )
1922         {   // kMinusOneOne
1923             sourcefile << "2.D0 * ( " << GetNameInput(i) << " - " << minInput[i] << "D0 ) / " << maxInput[i] - minInput[i] << "D0 - 1.0" << endl;
1924         }
1925         else
1926         {   //  kCR, kZeroOne
1927             sourcefile << "( " << GetNameInput(i) << " - " << minInput[i] << "D0 ) / " << maxInput[i] << "D0" << endl;
1928         }
1929     }
1930
1931     // Weights vector
1932     sourcefile << endl;
1933     sourcefile << "C --- Values of the weights" << endl;
1934     for(int i=0 ; i<nWeights ; i++)
1935     {
1936         sourcefile << "      VW" << i+1 << " = " << valW[i] << endl;
1937     }
1938     // Loop on hidden neurons
1939     sourcefile << endl;
1940     for(int member = 0; member < nHidden; member++) 
1941     {
1942         sourcefile << "C --- hidden neural number " << member+1 << endl;
1943         int CrtW = member * ( nInput + 2) + 3;
1944         sourcefile << "      VAct" << member+1 << " = VW" << CrtW++ << endl;
1945         for (int source = 0; source < nInput; source++)
1946         {
1947             sourcefile << "     1      + VW"<< CrtW++ << " * VXN" << GetNameInput(source) << endl;
1948         }
1949         sourcefile << endl;
1950
1951
1952         if( normType==0 )
1953         {   // kMinusOneOne
1954             sourcefile << "      VPot" << member+1 << " = 2.D0 / (1.D0 + DEXP(-2.D0 * VAct" << member+1 <<")) - 1.D0" << endl;
1955         }
1956         else
1957         {   //  kCR, kZeroOne
1958             sourcefile << "      VPot" << member+1 << " = 1.D0 / (1.D0 + DEXP(-1.D0 * VAct" << member+1 <<"))" << endl;
1959         }
1960         sourcefile << endl;
1961     }
1962
1963     // Ouput of the model
1964     sourcefile << "C --- Output" << endl;
1965     sourcefile << "      VOut = VW1" << endl;
1966     for(int source=0 ; source < nHidden ; source++)
1967     {
1968         int CrtW = source * ( nInput + 2) + 2;
1969         sourcefile << "     1    + VW"<< CrtW  << " * VPot" << source+1 << endl;
1970     }
1971
1972     // Denormalize Output
1973     sourcefile << endl;
1974     sourcefile << "C --- Pretraitment of the output" << endl;
1975     if( normType==0 )
1976     {   // kMinusOneOne
1977         sourcefile << "      VDelta = " << 0.5*(maxOutput[0]-minOutput[0]) << "D0 * ( VOut + 1.0D0)" << endl;
1978         sourcefile << "      " << GetNameOutput(0) << " = " << minOutput[0] << "D0 + VDelta" << endl;
1979
1980     }
1981     else
1982     {   //  kCR, kZeroOne
1983         sourcefile << "      " << GetNameOutput(0) << " = "<< minOutput[0] << "D0 + " << maxOutput[0] << "D0 * VOut;" << endl;
1984     }
1985
1986     sourcefile << endl;
1987     sourcefile << "C --- " << endl;
1988     sourcefile << "      RETURN" << endl;
1989     sourcefile << "      END" << endl;
1990
1991     sourcefile.close();
1992 }
1993
1994 /**
1995  * Export the current model as a NeuralNetwork function in a Python file.
1996  * @brief Specific to NeuralNetwork
1997  * @param file Name of the file  
1998  * @param functionName Name of the function  
1999  * @param header Header of the function  
2000  */
2001 void PMMLlib::ExportNeuralNetworkPython(std::string file, 
2002                                         std::string functionName, 
2003                                         std::string header)
2004 {
2005     string str(ExportNeuralNetworkPyStr(functionName, header));
2006     // Write the file
2007     ofstream exportfile(file.c_str()); 
2008     exportfile << str;
2009     exportfile.close();  
2010 }
2011
2012
2013 /**
2014  * Export the current model as a function in a Python string.
2015  * @brief Specific to NeuralNetwork
2016  * @param functionName Name of the function  
2017  * @param header Header of the function  
2018  * @return Function as a string
2019  */ 
2020 std::string PMMLlib::ExportNeuralNetworkPyStr(std::string functionName, 
2021                                               std::string header)
2022 {
2023     CheckNeuralNetwork();
2024     
2025     ostringstream out;
2026   
2027     // Get the different values required
2028     int nInput = GetNbInputs();
2029     int nOutput = GetNbOutputs();
2030     int nHidden = GetNbNeuronsAtLayer(0);
2031     int nNeurons = nInput+nOutput+nHidden;
2032     int nWeights = nHidden*(nInput+nOutput+1)+nOutput;
2033     int normType = GetNormalizationType();
2034     // Build min/max input/output vectors
2035     vector<double> minInput(nInput);
2036     vector<double> maxInput(nInput);
2037     vector<double> minOutput(nOutput);
2038     vector<double> maxOutput(nOutput);
2039     vector<double> valW(nWeights);
2040     fillVectorsForExport(nInput,nOutput,nHidden,normType,minInput,maxInput,minOutput,maxOutput,valW);
2041
2042     // Shebang et imports
2043     out << "#!/usr/bin/env python3" << endl;
2044     out << "# -*- coding: utf-8 -*-" << endl;
2045     out << endl;
2046     out << "from math import tanh, exp" << endl;
2047     out << endl;
2048
2049     // ActivationFunction
2050     if( normType==0 )
2051     {   // kMinusOneOne
2052         out << "def ActivationFunction(sum): " << endl;
2053         out << "    return tanh(sum); " << endl;
2054     }
2055     else
2056     {   //  kCR, kZeroOne
2057         out << "def ActivationFunction(sum): " << endl;
2058         out << "    return ( 1.0 / ( 1.0 + exp( -1.0 * sum ) ) ); " << endl;        
2059     }
2060
2061     out << endl;
2062     out << "def " << functionName <<"(param):" << endl;
2063     out << endl;
2064
2065     // header
2066     out << "    ############################## " << endl;
2067     out << "    #" << endl;
2068     // insert comments in header
2069     header = "    # " + header;
2070     size_t pos = 0;
2071     while ((pos = header.find("\n", pos)) != std::string::npos) 
2072     {
2073         header.replace(pos, 1, "\n    #");
2074         pos += 5;
2075     }
2076     out << header << endl;
2077     out << "    #" << endl;
2078     out << "    ############################## " << endl;
2079     out << endl;
2080
2081     // Initialisations
2082     out << "    nInput = " << nInput << ";" << endl;
2083     out << "    nOutput = " << nOutput << ";" << endl;
2084     out << "    nHidden = " <<  nHidden << ";" << endl;
2085     out << "    nNeurones = " <<  nNeurons << ";" << endl;
2086     out << "    " << functionName << "_act = [];" << endl;
2087     out << "    res = [];" << endl;    
2088     out << endl;
2089
2090     out << "    # --- Preprocessing of the inputs and outputs" << endl;
2091     out << "    " << functionName << "_minInput = [" << endl << "  ";
2092     out << "    " ;
2093     for(int i=0 ; i<nInput ; i++)
2094     {
2095         out << minInput[i] << ", ";
2096         if( (i+1)%5==0 )
2097         {
2098             out << endl ;
2099             out << "    " ;
2100         }
2101     }
2102     out << endl <<  "    ];" << endl;
2103
2104     out << "    " << functionName << "_minOutput = [" << endl << "    ";
2105     out << "    " << minOutput[0] ;
2106     out << endl << "    ];" << endl;
2107
2108     out << "    " << functionName << "_maxInput = [" << endl << "    ";
2109     for(int i=0 ; i<nInput ; i++)
2110     {
2111         out << maxInput[i] << ", ";
2112         if( (i+1)%5==0 )
2113         {
2114             out << endl;
2115             out << "    " ;
2116         }
2117     }
2118     out << endl << "    ];" << endl;
2119
2120     out << "    " << functionName << "_maxOutput = [" << endl << "    ";
2121     out << "    " << maxOutput[0] ;
2122     out << endl << "    ];" << endl;
2123
2124     // Weights vector
2125     out << "    # --- Values of the weights" << endl;
2126     out << "    " << functionName << "_valW = [" << endl << "    ";
2127     for(int i=0 ; i<nWeights ; i++)
2128     {
2129         out << valW[i] << ", ";
2130         if ( (i+1)%5 == 0 )
2131         {
2132             out << endl;
2133             out << "    " ;
2134         }
2135     }
2136     out << endl << "    ];"<<endl;
2137
2138     out << "    # --- Constants" << endl;
2139     out << "    indNeurone = 0;" << endl;
2140     out << endl;
2141     
2142     // couche entree
2143     out << "    # --- Input Layers" << endl;
2144     out << "    for i in range(nInput) :" << endl;
2145     if( normType==0 )
2146     {   // kMinusOneOne
2147         out << "        " << functionName << "_act.append( 2.0 * ( param[i] - "
2148                    << functionName << "_minInput[i] ) / ( " << functionName << "_maxInput[i] - "
2149                    << functionName << "_minInput[i] ) - 1.0 ) ;"
2150                    << endl;                  
2151     }
2152     else
2153     {   //  kCR, kZeroOne
2154         out << "        " << functionName << "_act.append( ( param[i] - "
2155                    << functionName << "_minInput[i] ) / " << functionName << "_maxInput[i] ) ;"
2156                    << endl;
2157     }   
2158     out << "        indNeurone += 1 ;" << endl;
2159     out << "        pass" << endl;
2160     
2161     // couche cachee
2162     out << endl;
2163     out << "    # --- Hidden Layers" << endl;
2164     out << "    for member in range(nHidden):" << endl;
2165     out << "        CrtW = member * ( nInput + 2) + 2;" << endl;
2166     out << "        sum = " << functionName << "_valW[CrtW];" << endl;
2167     out << "        CrtW += 1 ;" << endl;     
2168     out << "        for source in range(nInput) :" << endl;
2169     out << "            sum += " << functionName << "_act[source] * " << functionName << "_valW[CrtW];" << endl;
2170     out << "            CrtW += 1 ;" << endl; 
2171     out << "            pass" << endl;
2172     out << "        " << functionName << "_act.append( ActivationFunction(sum) ) ;" << endl;
2173     out << "        indNeurone += 1 ;" << endl;
2174     out << "        pass" << endl;
2175     out << endl;
2176     
2177     // couche sortie
2178     out << "    # --- Output"<<endl;
2179     out << "    for member in range(nOutput):" << endl; 
2180     out << "        sum = " << functionName << "_valW[0];" << endl;
2181     out << "        for source in range(nHidden):" << endl;
2182     out << "            CrtW = source * ( nInput + 2) + 1;"<<endl;
2183     out << "            sum += " << functionName << "_act[nInput+source] * " << functionName << "_valW[CrtW];" << endl;
2184     out << "            pass" << endl;
2185     out << "        " << functionName << "_act.append( sum );" << endl;
2186     out << "        indNeurone += 1 ;" << endl;
2187     if( normType==0 )
2188     {   // kMinusOneOne
2189         out << "        res[member] = " << functionName
2190                    << "_minOutput[member] + 0.5 * ( " << functionName
2191                    << "_maxOutput[member] - " << functionName
2192                    << "_minOutput[member] ) * ( sum + 1.0);" << endl;
2193     }
2194     else
2195     {   //  kCR, kZeroOne
2196         out << "        res.append( " << functionName
2197                    << "_minOutput[member] + " << functionName
2198                    << "_maxOutput[member] * sum );" << endl;
2199     }
2200     out << "        pass" << endl;
2201     out << endl;
2202     
2203     // return result
2204     out << "    return res;" << endl << endl;
2205     out << endl;    
2206    
2207     return out.str(); 
2208 }
2209
2210 //**************************************************************
2211 //                                                             *
2212 //                                                             *
2213 //                                                             *
2214 //  méthodes propres au RegressionModel                        *
2215 //                                                             *
2216 //                                                             *
2217 //                                                             *
2218 //**************************************************************
2219
2220 /**
2221  * Check if the current model type is kLR.
2222  *    \brief Called in all methods specific to the RegressionModel model.
2223  *    \brief Throw an exception if the model type is not kLR.
2224  */
2225 void PMMLlib::CheckRegression()
2226 {
2227     if ( _currentModelType != kLR )
2228         throw string("Use this method with Regression models."); 
2229 }
2230
2231 /**
2232  * Get the pointeur to the regression model node.
2233  * @param name Name of the regression model 
2234  * @return Pointer to the XML node
2235  */
2236 xmlNodePtr PMMLlib::GetRegressionPtr(std::string name)
2237 {
2238     return GetPtr(name, GetTypeString() );  
2239 }   
2240
2241 /**
2242  * Add a RegressionModel to the root node
2243  * @brief Specific to RegressionModel
2244  * @param modelName Name of the model (Value of property "modelName")
2245  * @param functionName Value of property "functionName"
2246  * @param targetFieldName Value of Property "targetFieldName"
2247  */
2248 void PMMLlib::AddRegressionModel(std::string modelName, 
2249                                  PMMLMiningFunction functionName, 
2250                                  std::string targetFieldName)
2251 {
2252     _currentModelType = kLR;
2253     _currentModelName = modelName;
2254     // Check regression after setting model type!
2255     CheckRegression();
2256     
2257     string function;
2258     switch(functionName)
2259     {
2260         case kREGRESSION:
2261             function = "regression"; 
2262             break;
2263     }
2264     xmlNodePtr netNode = xmlNewChild(_rootNode, 0, (const xmlChar*)"RegressionModel", 0);     
2265     xmlNewProp(netNode, (const xmlChar*)"functionName", (const xmlChar*)(function.c_str()) );
2266     xmlNewProp(netNode, (const xmlChar*)"modelName", (const xmlChar*)(_currentModelName.c_str()) );
2267     xmlNewProp(netNode, (const xmlChar*)"targetFieldName", (const xmlChar*)(targetFieldName.c_str()) );
2268     _currentModelNode = netNode ;
2269 }
2270
2271 /**
2272  * Add a RegressionTable to the Regression model.
2273  * @brief No property "intercept" will be set.
2274  * @brief Specific to RegressionModel 
2275  */
2276 void PMMLlib::AddRegressionTable()
2277 {
2278     CheckRegression();  
2279     xmlNodePtr tableNode = xmlNewChild(_currentModelNode, 0, (const xmlChar*)"RegressionModel", 0);
2280     _currentNode = tableNode;
2281 }
2282
2283 /**
2284  * Add a RegressionTable to the Regression model with a given value of property "intercept".
2285  * @brief Specific to RegressionModel 
2286  * @param intercept Value of property "intercept"
2287  */
2288 void PMMLlib::AddRegressionTable(double intercept)
2289 {
2290     CheckRegression();
2291
2292     stringstream ss;
2293     xmlNodePtr tableNode = xmlNewChild(_currentModelNode, 0, (const xmlChar*)"RegressionTable", 0);
2294     if(intercept!=0.0)
2295     {
2296         ss << scientific << intercept;
2297         xmlNewProp(tableNode, (const xmlChar*)"intercept", (const xmlChar*)(ss.str().c_str()) );
2298     }
2299     _currentNode = tableNode;
2300 }
2301
2302 /**
2303  * Add a numeric predictor to the Regression model.
2304  * @brief Specific to RegressionModel 
2305  * @param neuronName  Value of property "name"
2306  * @param exponent Value of property "exponent"
2307  * @param coefficient Value of property "coefficient"
2308  */
2309 void PMMLlib::AddNumericPredictor(std::string neuronName, 
2310                                   int exponent, 
2311                                   double coefficient)
2312 {
2313     CheckRegression(); 
2314     stringstream ss;
2315     xmlNodePtr numPrecNode = xmlNewChild(_currentNode, 0, (const xmlChar*)"NumericPredictor", 0);
2316     xmlNewProp(numPrecNode, (const xmlChar*)"name", (const xmlChar*)(neuronName.c_str()) );
2317     ss.str("");  ss << exponent;
2318     xmlNewProp(numPrecNode, (const xmlChar*)"exponent", (const xmlChar*)(ss.str().c_str()) );
2319     ss.str("");  ss << scientific << coefficient;
2320     xmlNewProp(numPrecNode, (const xmlChar*)"coefficient", (const xmlChar*)(ss.str().c_str()) );
2321 }
2322
2323 /**
2324  * Add a predictor term to the Regression model.
2325  * @brief Specific to RegressionModel  
2326  * @param coefficient Value of property "coefficient"
2327  * @param fieldRef List of values for property "field", one per FieldRef to add to the PredictorTerm
2328  */
2329 void PMMLlib::AddPredictorTerm(double coefficient,
2330                                std::vector<std::string> fieldRef)
2331 {
2332     CheckRegression();
2333     stringstream ss;
2334     xmlNodePtr predTermNode = xmlNewChild(_currentNode, 0, (const xmlChar*)"PredictorTerm", 0);
2335     ss.str("");  ss << scientific << coefficient;
2336     xmlNewProp(predTermNode, (const xmlChar*)"coefficient", (const xmlChar*)(ss.str().c_str()) );
2337     vector<string>::iterator it;
2338     for(it=fieldRef.begin() ; it!=fieldRef.end() ; it++)
2339     {
2340         xmlNodePtr fieldRefNode = xmlNewChild(predTermNode, 0, (const xmlChar*)"FieldRef", 0);
2341         ss.str(""); ss << (*it);
2342         xmlNewProp(fieldRefNode, (const xmlChar*)"field", (const xmlChar*)(ss.str().c_str()) );
2343     }
2344 }
2345
2346 /**
2347  * Check if the RegressionTable has a property called "intercept".
2348  * @brief Specific to RegressionModel  
2349  * @return true if it has, false otherwise
2350  */
2351 bool PMMLlib::HasIntercept()
2352 {
2353     CheckRegression();
2354     bool b = false;   
2355     xmlNodePtr tableNode = GetChildByName(_currentModelNode,"RegressionTable");
2356     if ( tableNode == NULL )
2357       return b;         
2358     xmlChar *xp = _stringToXmlChar("intercept");
2359     xmlChar * attr ;
2360     attr = xmlGetProp(tableNode, xp);
2361     if ( attr ) 
2362     {
2363         xmlFree(attr);
2364         xmlFree(xp);  
2365         return true;
2366     }  
2367     xmlFree(xp);  
2368     return false;
2369 }
2370
2371 /**
2372  * Get the value of property "intercept" in the RegressionTable.
2373  * @brief Specific to RegressionModel  
2374  * @return Value of property "intercept"
2375  */
2376 double PMMLlib::GetRegressionTableIntercept()
2377 {
2378     CheckRegression();
2379     double reg = 0.;
2380     xmlNodePtr tableNode = GetChildByName(_currentModelNode,"RegressionTable");
2381     if ( tableNode == NULL )
2382         return reg; 
2383     string strValue = _getProp(tableNode, string("intercept") );
2384     return atof(strValue.c_str());
2385 }
2386
2387 /**
2388  * Get the number of numeric predictors.
2389  * @brief Specific to RegressionModel  
2390  * @return Number of numeric predictors
2391  */
2392 int PMMLlib::GetNumericPredictorNb()
2393 {
2394     CheckRegression();
2395     
2396     int nb=0;   
2397     xmlNodePtr tableNode = GetChildByName(_currentModelNode,"RegressionTable");
2398     if ( tableNode == NULL )
2399         return nb;
2400     xmlNodePtr numPredNodes =  tableNode->children;
2401     while (numPredNodes != NULL )
2402     {
2403         if ( string((const char*)(numPredNodes->name)) == "NumericPredictor" )
2404             nb++;
2405         numPredNodes = numPredNodes->next;
2406     }    
2407     return nb;
2408 }
2409
2410 /**
2411  * Get the number of predictor terms.
2412  * @brief Specific to RegressionModel  
2413  * @return Number of predictor terms
2414  */
2415 int PMMLlib::GetPredictorTermNb()
2416 {
2417     CheckRegression();
2418     int nb=0;    
2419     xmlNodePtr tableNode = GetChildByName(_currentModelNode,"RegressionTable");
2420     if ( tableNode == NULL )
2421       return nb;  
2422     xmlNodePtr numPredNodes =  tableNode->children;
2423     while ( numPredNodes != NULL )      
2424     {
2425         if ( string((const char*)(numPredNodes->name)) == "PredictorTerm" )
2426             nb++;
2427         numPredNodes = numPredNodes->next;
2428     }    
2429     return nb;
2430 }
2431
2432 /**
2433  * Get the name of the numeric predictor given by its index.
2434  * @brief Specific to RegressionModel  
2435  * @param num_pred_index Index of the numeric predictor
2436  * @return Name of the numeric predictor
2437  */
2438 std::string PMMLlib::GetNumericPredictorName(int num_pred_index)
2439 {
2440     CheckRegression();
2441     string strName("");   
2442     xmlNodePtr numPredNodes = GetChildByName(_currentModelNode,"RegressionTable");
2443     if ( numPredNodes == NULL )
2444         return strName;
2445     
2446     numPredNodes = GetChildByName(numPredNodes,"NumericPredictor");
2447     if ( numPredNodes == NULL )
2448         return strName;
2449     // Positionnement sur la bonne sortie PredictorTerm
2450     for(int i=0;i<num_pred_index;i++)
2451     {
2452         numPredNodes = numPredNodes->next;
2453         if ( numPredNodes == NULL || 
2454              string((const char*)(numPredNodes->name)) != "NumericPredictor" )
2455             return strName;        
2456     }        
2457     strName = _getProp(numPredNodes, string("name"));
2458     return strName;
2459 }
2460
2461 /**
2462  * Get the name of the predictor term given by its index.
2463  * @brief Specific to RegressionModel  
2464  * @param pred_term_index Index of the predictor term 
2465  * @return Name of the predictor term
2466  */
2467 std::string PMMLlib::GetPredictorTermName(int pred_term_index)
2468 {
2469     CheckRegression();
2470     string strName("");   
2471     xmlNodePtr fieldRefNodes = GetChildByName(_currentModelNode,"RegressionTable");
2472     if ( fieldRefNodes == NULL )
2473         return strName;    
2474     
2475     fieldRefNodes = GetChildByName(fieldRefNodes,"PredictorTerm");
2476     if ( fieldRefNodes == NULL )
2477         return strName;
2478     // Positionnement sur la bonne sortie
2479     for(int i=0;i<pred_term_index;i++)
2480     {
2481         fieldRefNodes = fieldRefNodes->next;
2482         if ( fieldRefNodes == NULL || 
2483             string((const char*)(fieldRefNodes->name)) != "PredictorTerm" )
2484             return strName;        
2485     }  
2486     
2487     fieldRefNodes = fieldRefNodes->children;
2488     while (fieldRefNodes != NULL)
2489     {
2490         strName += _getProp(fieldRefNodes, string("field"));
2491         fieldRefNodes = fieldRefNodes->next;
2492     }        
2493     return strName;
2494 }
2495
2496 /**
2497  * Get the coefficient of the numeric predictor given by its index.
2498  * @brief (The coefficient is the value of property "coefficient")
2499  * @brief Specific to RegressionModel  
2500  * @param num_pred_index Index of the numeric predictor
2501  * @return Coefficient of the numeric predictor
2502  */
2503 double PMMLlib::GetNumericPredictorCoefficient(int num_pred_index)
2504 {
2505     CheckRegression();
2506     
2507     double coef = 0.;  
2508     xmlNodePtr numPredNodes = GetChildByName(_currentModelNode,"RegressionTable");
2509     if ( numPredNodes == NULL )
2510         return coef;    
2511     numPredNodes = GetChildByName(numPredNodes,"NumericPredictor");
2512     if ( numPredNodes == NULL )
2513         return coef;
2514     // Positionnement sur la bonne sortie
2515     for(int i=0;i<num_pred_index;i++)
2516     {
2517         numPredNodes = numPredNodes->next;
2518         if ( numPredNodes == NULL || 
2519              string((const char*)(numPredNodes->name)) != "NumericPredictor" )
2520             return coef;        
2521     }  
2522     string strValue = _getProp(numPredNodes, string("coefficient")); 
2523     coef = atof(strValue.c_str());
2524     return coef;  
2525 }
2526
2527 /**
2528  * Get the coefficient of the predictor term given by its index.
2529  * @brief (The coefficient is the value of property "coefficient")
2530  * @brief Specific to RegressionModel  
2531  * @param pred_term_index Index of the predictor term
2532  * @return Coefficient of the predictor term
2533  */
2534 double PMMLlib::GetPredictorTermCoefficient(int pred_term_index)
2535 {
2536     CheckRegression();
2537     
2538     double coef = 0.;   
2539     xmlNodePtr predTermNodes = GetChildByName(_currentModelNode,"RegressionTable");
2540     if ( predTermNodes == NULL )
2541         return coef;    
2542     predTermNodes = GetChildByName(predTermNodes,"PredictorTerm");
2543     if ( predTermNodes == NULL )
2544         return coef;
2545     // Positionnement sur la bonne sortie
2546     for(int i=0;i<pred_term_index;i++)
2547     {
2548         predTermNodes = predTermNodes->next;
2549         if ( predTermNodes == NULL || 
2550              string((const char*)(predTermNodes->name)) != "PredictorTerm" )
2551             return coef;        
2552     }  
2553     string strValue = _getProp(predTermNodes, string("coefficient")); 
2554     coef = atof(strValue.c_str());
2555     return coef;
2556 }
2557
2558 /**
2559  * Get the number of FieldRef for the predictor term given by its index.
2560  * @brief Specific to RegressionModel   
2561  * @param indsex Index of the predictor term
2562  * @return Number of FieldRef
2563  */
2564 int PMMLlib::GetPredictorTermFieldRefNb(int index)
2565 {
2566     CheckRegression();
2567     
2568     int nb=0;
2569     xmlNodePtr fieldRefNodes = GetChildByName(_currentModelNode,"RegressionTable");
2570     if ( fieldRefNodes == NULL )
2571       return nb;
2572     fieldRefNodes = GetChildByName(fieldRefNodes,"PredictorTerm");
2573     if ( fieldRefNodes == NULL )
2574         return nb;
2575     // Positionnement sur la bonne sortie
2576     for(int i=0;i<index;i++)
2577     {
2578         fieldRefNodes = fieldRefNodes->next;
2579         if ( fieldRefNodes == NULL || 
2580              string((const char*)(fieldRefNodes->name)) != "PredictorTerm" )
2581             return nb;        
2582     }  
2583     fieldRefNodes = fieldRefNodes->children;
2584     while (fieldRefNodes != NULL)
2585     {
2586         nb++;
2587         fieldRefNodes = fieldRefNodes->next;
2588     }    
2589     return nb;
2590 }
2591
2592 /**
2593  * Get the name of the field_index-th FieldRef for the pred_term_index-th predictor term.
2594  * @brief (The name is the value of property "field")
2595  * @brief Specific to RegressionModel   
2596  * @param pred_term_index Index of the predictor term
2597  * @param field_index Index of the FieldRef
2598  * @return Name of the FieldRef
2599  */
2600 std::string PMMLlib::GetPredictorTermFieldRefName(int pred_term_index, int field_index)   
2601 {
2602     CheckRegression();
2603     
2604     string strName("");    
2605     xmlNodePtr fieldRefNodes = GetChildByName(_currentModelNode,"RegressionTable");
2606     if ( fieldRefNodes == NULL )
2607       return strName;
2608     fieldRefNodes = GetChildByName(fieldRefNodes,"PredictorTerm");
2609     if ( fieldRefNodes == NULL )
2610         return strName;
2611     // Positionnement sur la bonne sortie PredictorTerm
2612     for(int i=0;i<pred_term_index;i++)
2613     {
2614         fieldRefNodes = fieldRefNodes->next;
2615         if ( fieldRefNodes == NULL || 
2616              string((const char*)(fieldRefNodes->name)) != "PredictorTerm" )
2617             return strName;        
2618     }  
2619     fieldRefNodes = fieldRefNodes->children;
2620     if ( fieldRefNodes == NULL )
2621         return strName;
2622     // Positionnement sur la bonne sortie FieldRef
2623     for(int i=0;i<field_index;i++)
2624     {
2625         fieldRefNodes = fieldRefNodes->next;
2626         if ( fieldRefNodes == NULL )
2627             return strName;        
2628     }     
2629     strName = _getProp(fieldRefNodes, string("field"));
2630     return strName;
2631 }
2632
2633 /**
2634  * Export the current model as a NeuralNetwork function in a Cpp file.
2635  * @brief Specific to RegressionModel  
2636  * @param file Name of the file  
2637  * @param functionName Name of the function  
2638  * @param header Header of the function  
2639  */
2640 void PMMLlib::ExportLinearRegressionCpp(std::string file, 
2641                                         std::string functionName, 
2642                                         std::string header)
2643 {
2644     CheckRegression();
2645
2646     // Write the file
2647     ofstream exportfile(file.c_str());
2648     
2649     exportfile << "void " << functionName <<"(double *param, double *res)" << endl;
2650     exportfile << "{" << endl;    
2651     // header
2652     exportfile << "  ////////////////////////////// " << endl;
2653     exportfile << "  //" << endl;
2654     // insert comments in header
2655     header = "  // " + header;
2656     size_t pos = 0;
2657     while ((pos = header.find("\n", pos)) != std::string::npos) 
2658     {
2659         header.replace(pos, 1, "\n  //");
2660         pos += 5;
2661     }
2662     exportfile << header << endl;
2663     exportfile << "  //" << endl;
2664     exportfile << "  ////////////////////////////// " << endl << endl;  
2665
2666     double intercept = 0.0;    
2667     if ( HasIntercept() ) 
2668     {
2669         exportfile << "  // Intercept"<< endl;
2670         intercept = GetRegressionTableIntercept();
2671     }
2672     else 
2673         exportfile << "  // No Intercept"<< endl;
2674     exportfile << "  double y = " << intercept << ";";
2675     exportfile << endl << endl;
2676      
2677     int nPred = GetNumericPredictorNb();
2678     for (int i=0; i<nPred; i++)
2679     {
2680        exportfile << "  // Attribute : " << GetNumericPredictorName(i) << endl;
2681        exportfile << "  y += param["<<i<<"]*" << GetNumericPredictorCoefficient(i) << ";";
2682        exportfile << endl << endl;
2683     }
2684     nPred = GetPredictorTermNb();
2685     for (int i=0; i<nPred; i++)
2686     {
2687        exportfile << "  // Attribute : " << GetPredictorTermName(i) << endl;
2688        exportfile << "  y += param["<<(i+nPred)<<"]*" << GetPredictorTermCoefficient(i) << ";";
2689        exportfile << endl << endl;
2690     }    
2691     
2692     exportfile << "  // Return the value"<< endl;
2693     exportfile << "  res[0] = y;" << endl;
2694     exportfile << "}" << endl;    
2695     exportfile.close(); 
2696 }
2697
2698 /**
2699  * Export the current model as a NeuralNetwork function in a Fortran file.
2700  * @brief Specific to Regression
2701  * @param file Name of the file  
2702  * @param functionName Name of the function  
2703  * @param header Header of the function  
2704  */
2705 void PMMLlib::ExportLinearRegressionFortran(std::string file, 
2706                                             std::string functionName, 
2707                                             std::string header)
2708 {
2709     CheckRegression();
2710
2711     int nNumPred = GetNumericPredictorNb();
2712     int nPredTerm = GetPredictorTermNb(); 
2713     vector<string>strParam(nNumPred+nPredTerm);
2714     for(int i=0; i<(nNumPred+nPredTerm); i++)
2715     {
2716         strParam[i] = "P" + NumberToString(i) ;
2717     }    
2718     
2719     // Write the file
2720     ofstream exportfile(file.c_str());
2721     
2722     exportfile << "      SUBROUTINE " << functionName <<"(";
2723     for(int i=0; i<(nNumPred+nPredTerm); i++)
2724     {
2725         exportfile << strParam[i] << ", ";
2726     }    
2727     exportfile << "RES)" << endl;    
2728  
2729     // header
2730     exportfile << "C --- *********************************************" << endl;
2731     exportfile << "C --- " << endl;
2732     // insert comments in header
2733     header = "C ---  " + header;
2734     size_t pos = 0;
2735     while ((pos = header.find("\n", pos)) != std::string::npos) 
2736     {
2737         header.replace(pos, 1, "\nC --- ");
2738         pos += 5;
2739     }
2740     exportfile << header << endl;
2741     exportfile << "C --- " << endl;
2742     exportfile << "C --- *********************************************" << endl << endl;  
2743
2744     exportfile << "      IMPLICIT DOUBLE PRECISION (P)" << endl;
2745     exportfile << "      DOUBLE PRECISION RES" << endl;    
2746     exportfile << "      DOUBLE PRECISION Y" << endl;
2747     exportfile << endl;    
2748     
2749     double intercept = 0.0;    
2750     if ( HasIntercept() ) 
2751     {
2752         exportfile << "C --- Intercept"<< endl;
2753         intercept = GetRegressionTableIntercept();
2754     }
2755     else 
2756         exportfile << "C --- No Intercept"<< endl;
2757     exportfile << "      Y = " << intercept << ";";
2758     exportfile << endl << endl;
2759      
2760     for (int i=0; i<nNumPred; i++)
2761     {
2762        exportfile << "C --- Attribute : " << GetNumericPredictorName(i) << endl;
2763        exportfile << "      Y += P["<<i<<"]*" << GetNumericPredictorCoefficient(i) << ";";
2764        exportfile << endl << endl;
2765     }
2766
2767     for (int i=0; i<nPredTerm; i++)
2768     {
2769        exportfile << "C --- Attribute : " << GetPredictorTermName(i) << endl;
2770        exportfile << "      Y += P["<<(i+nNumPred)<<"]*" << GetPredictorTermCoefficient(i) << ";";
2771        exportfile << endl << endl;
2772     }    
2773     
2774     exportfile << "C --- Return the value"<< endl;
2775     exportfile << "      RES = Y " << endl;
2776     exportfile << "      RETURN" << endl;    
2777     exportfile << "      END" << endl;    
2778     exportfile.close(); 
2779 }
2780
2781 /**
2782  * Export the current model as a NeuralNetwork function in a Python file.
2783  * @brief Specific to Regression
2784  * @param file Name of the file  
2785  * @param functionName Name of the function  
2786  * @param header Header of the function  
2787  */
2788 void PMMLlib::ExportLinearRegressionPython(std::string file, 
2789                                            std::string functionName, 
2790                                            std::string header)
2791 {
2792     string str(ExportLinearRegressionPyStr(functionName, header));
2793     // Write the file
2794     ofstream exportfile(file.c_str()); 
2795     exportfile << str;
2796     exportfile.close(); 
2797 }
2798
2799 /**
2800  * Export the current model as a NeuralNetwork function in a Python string.
2801  * @brief Specific to Regression 
2802  * @param functionName Name of the function  
2803  * @param header Header of the function  
2804  */
2805 std::string PMMLlib::ExportLinearRegressionPyStr(std::string functionName, 
2806                                                  std::string header)
2807 {
2808     CheckRegression();
2809
2810     ostringstream out;
2811     
2812     // Shebang et imports
2813     out << "#!/usr/bin/env python3" << endl;
2814     out << "# -*- coding: utf-8 -*-" << endl;
2815     out << endl;    
2816     
2817     // Function
2818     out << "def " << functionName <<"(param):" << endl;
2819     out << endl; 
2820     
2821     // header
2822     out << "    ############################## " << endl;
2823     out << "    # " << endl;
2824     // insert comments in header
2825     header = "    # " + header;
2826     size_t pos = 0;
2827     while ((pos = header.find("\n", pos)) != std::string::npos) 
2828     {
2829         header.replace(pos, 1, "\n    #");
2830         pos += 5;
2831     }    
2832     out << header << endl;
2833     out << "    # " << endl;
2834     out << "    ############################## " << endl << endl;  
2835
2836     double intercept = 0.0;    
2837     if ( HasIntercept() ) 
2838     {
2839         out << "    #  Intercept"<< endl;
2840         intercept = GetRegressionTableIntercept();
2841     }
2842     else 
2843         out << "    #  No Intercept"<< endl;
2844     out << "    y = " << intercept << ";";
2845     out << endl << endl;
2846      
2847     int nPred = GetNumericPredictorNb();
2848     for (int i=0; i<nPred; i++)
2849     {
2850        out << "    #  Attribute : " << GetNumericPredictorName(i) << endl;
2851        out << "    y += param["<<i<<"]*" << GetNumericPredictorCoefficient(i) << ";";
2852        out << endl << endl;
2853     }
2854     nPred = GetPredictorTermNb();
2855     for (int i=0; i<nPred; i++)
2856     {
2857        out << "    #  Attribute : " << GetPredictorTermName(i) << endl;
2858        out << "    y += param["<<(i+nPred)<<"]*" << GetPredictorTermCoefficient(i) << ";";
2859        out << endl << endl;
2860     }    
2861     
2862     out << "    #  Return the value"<< endl;
2863     out << "    return [y];" << endl;
2864         
2865     return out.str() ;
2866 }
2867
2868 /**
2869  * Read the structure of the regression model
2870  * @brief Specific to RegressionModel 
2871  * @return Structure read
2872  */
2873 std::string PMMLlib::ReadRegressionStructure()
2874 {   
2875     CheckRegression(); 
2876
2877     string structure("");    
2878     string structureActive("");
2879     string structurePredicted("@");
2880     int nPred = 0;
2881     xmlNodePtr mNode = GetChildByName(_currentModelNode,"MiningSchema");
2882     if ( mNode != NULL )
2883     {
2884         xmlNodePtr dNode = GetChildByName(mNode,"MiningField");     
2885         while (dNode != NULL)
2886         {
2887             string name = _getProp(dNode, string("name"));
2888             string usage = _getProp(dNode, string("usageType"));
2889             if ( usage == "active" )
2890             {
2891                 structureActive += name;
2892                 structureActive += ":"; 
2893             }    
2894             else if ( usage == "predicted" )
2895             {
2896                 structurePredicted += name;
2897                 structurePredicted += ":"; 
2898                 nPred++;
2899             }                 
2900             dNode = dNode->next;             
2901         }
2902         // Delete the last ":"
2903         if ( structureActive.length() > 0 )
2904             structureActive.erase(structureActive.size()-1);        
2905         structurePredicted.erase(structurePredicted.size()-1);
2906     }
2907     std::ostringstream oss;
2908     oss << nPred;   
2909     structure = structureActive + ","  + oss.str() + "," + structurePredicted;
2910     return structure;
2911 }
2912
2913 } // end of namespace
2914
2915