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