From 00e3eae1b59a083a31f34e1c6a1b53ca83c72c8b Mon Sep 17 00:00:00 2001 From: lhelgoualch Date: Thu, 19 Dec 2013 15:45:14 +0000 Subject: [PATCH] PMML feature --- src/pmml/CMakeLists.txt | 146 + src/pmml/PMMLlib.cxx | 2916 +++++++++++++++++ src/pmml/PMMLlib.hxx | 301 ++ src/pmml/PMMLwin.hxx | 38 + src/pmml/Test/BasicMainTest.hxx | 96 + src/pmml/Test/CMakeLists.txt | 98 + src/pmml/Test/PMMLBasicsTest.hxx | 30 + src/pmml/Test/PMMLBasicsTest1.cxx | 1068 ++++++ src/pmml/Test/PMMLBasicsTest1.hxx | 284 ++ src/pmml/Test/TestPMML.cxx | 25 + src/pmml/Test/tools.cxx | 47 + src/pmml/Test/tools.hxx | 30 + src/pmml/doc/CMakeLists.txt | 21 + src/pmml/doc/doxygen/CMakeLists.txt | 43 + src/pmml/doc/doxygen/Doxyfile_pmml_user.in | 200 ++ src/pmml/doc/doxygen/doxfiles/cppexamples.dox | 367 +++ src/pmml/doc/doxygen/doxfiles/install.dox | 28 + src/pmml/doc/doxygen/doxfiles/intro.dox | 37 + src/pmml/doc/doxygen/doxfiles/pmml.dox | 14 + src/pmml/doc/doxygen/doxfiles/pyexamples.dox | 77 + src/pmml/doc/doxygen/images/head.png | Bin 0 -> 78545 bytes src/pmml/doc/doxygen/static/footer.html | 14 + src/pmml/doc/doxygen/static/header.html.in | 23 + src/pmml/doc/doxygen/static/salome_extra.css | 29 + src/pmml/pmml_swig/CMakeLists.txt | 68 + src/pmml/pmml_swig/PMML.i | 61 + src/pmml/pmml_swig/PMMLBasicsTest.py | 86 + src/pmml/pmml_swig/PMMLsalome.i | 40 + src/pmml/resources/CMakeLists.txt | 45 + src/pmml/resources/ann_model.pmml | 124 + src/pmml/resources/ann_model_2.pmml | 132 + src/pmml/resources/lr_model.pmml | 38 + src/pmml/resources/lr_model_2.pmml | 38 + src/pmml/resources/no_model.pmml | 144 + src/pmml/resources/two_models_ann_lr.pmml | 144 + src/pmml/resources/unittest_ref_ann_model.cpp | 67 + src/pmml/resources/unittest_ref_ann_model.f | 64 + src/pmml/resources/unittest_ref_ann_model.py | 82 + src/pmml/resources/unittest_ref_lr_model.cpp | 27 + src/pmml/resources/unittest_ref_lr_model.f | 31 + src/pmml/resources/unittest_ref_lr_model.py | 29 + src/pmml/resources/win32_ann_model.pmml | 124 + src/pmml/resources/win32_lr_model.pmml | 38 + src/yacsloader/pmml/BasicMainTest.hxx | 96 + src/yacsloader/pmml/CMakeLists.txt | 54 + src/yacsloader/pmml/TestYACSPMML.cxx | 25 + src/yacsloader/pmml/YACSPMMLBasicsTest.hxx | 30 + src/yacsloader/pmml/YACSPMMLBasicsTest1.cxx | 219 ++ src/yacsloader/pmml/YACSPMMLBasicsTest1.hxx | 56 + .../samples/pmml_tann_exportFunctionPMML.pmml | 142 + .../pmml_tann_tlr_exportFunctionPMML.pmml | 147 + .../samples/pmml_tlr_exportFunctionPMML.pmml | 41 + src/yacsloader/samples/schemaANN2.xml | 412 +++ src/yacsloader/samples/schemaANNLR2.xml | 569 ++++ src/yacsloader/samples/schemaLR2.xml | 414 +++ .../samples/schemaPmmlDoesNotExist.xml | 411 +++ 56 files changed, 9930 insertions(+) create mode 100755 src/pmml/CMakeLists.txt create mode 100755 src/pmml/PMMLlib.cxx create mode 100755 src/pmml/PMMLlib.hxx create mode 100755 src/pmml/PMMLwin.hxx create mode 100755 src/pmml/Test/BasicMainTest.hxx create mode 100755 src/pmml/Test/CMakeLists.txt create mode 100755 src/pmml/Test/PMMLBasicsTest.hxx create mode 100755 src/pmml/Test/PMMLBasicsTest1.cxx create mode 100755 src/pmml/Test/PMMLBasicsTest1.hxx create mode 100755 src/pmml/Test/TestPMML.cxx create mode 100755 src/pmml/Test/tools.cxx create mode 100755 src/pmml/Test/tools.hxx create mode 100755 src/pmml/doc/CMakeLists.txt create mode 100755 src/pmml/doc/doxygen/CMakeLists.txt create mode 100755 src/pmml/doc/doxygen/Doxyfile_pmml_user.in create mode 100755 src/pmml/doc/doxygen/doxfiles/cppexamples.dox create mode 100755 src/pmml/doc/doxygen/doxfiles/install.dox create mode 100755 src/pmml/doc/doxygen/doxfiles/intro.dox create mode 100755 src/pmml/doc/doxygen/doxfiles/pmml.dox create mode 100755 src/pmml/doc/doxygen/doxfiles/pyexamples.dox create mode 100755 src/pmml/doc/doxygen/images/head.png create mode 100755 src/pmml/doc/doxygen/static/footer.html create mode 100755 src/pmml/doc/doxygen/static/header.html.in create mode 100755 src/pmml/doc/doxygen/static/salome_extra.css create mode 100755 src/pmml/pmml_swig/CMakeLists.txt create mode 100755 src/pmml/pmml_swig/PMML.i create mode 100755 src/pmml/pmml_swig/PMMLBasicsTest.py create mode 100755 src/pmml/pmml_swig/PMMLsalome.i create mode 100755 src/pmml/resources/CMakeLists.txt create mode 100755 src/pmml/resources/ann_model.pmml create mode 100755 src/pmml/resources/ann_model_2.pmml create mode 100755 src/pmml/resources/lr_model.pmml create mode 100755 src/pmml/resources/lr_model_2.pmml create mode 100755 src/pmml/resources/no_model.pmml create mode 100755 src/pmml/resources/two_models_ann_lr.pmml create mode 100755 src/pmml/resources/unittest_ref_ann_model.cpp create mode 100755 src/pmml/resources/unittest_ref_ann_model.f create mode 100755 src/pmml/resources/unittest_ref_ann_model.py create mode 100755 src/pmml/resources/unittest_ref_lr_model.cpp create mode 100755 src/pmml/resources/unittest_ref_lr_model.f create mode 100755 src/pmml/resources/unittest_ref_lr_model.py create mode 100755 src/pmml/resources/win32_ann_model.pmml create mode 100755 src/pmml/resources/win32_lr_model.pmml create mode 100755 src/yacsloader/pmml/BasicMainTest.hxx create mode 100755 src/yacsloader/pmml/CMakeLists.txt create mode 100755 src/yacsloader/pmml/TestYACSPMML.cxx create mode 100755 src/yacsloader/pmml/YACSPMMLBasicsTest.hxx create mode 100755 src/yacsloader/pmml/YACSPMMLBasicsTest1.cxx create mode 100755 src/yacsloader/pmml/YACSPMMLBasicsTest1.hxx create mode 100644 src/yacsloader/samples/pmml_tann_exportFunctionPMML.pmml create mode 100644 src/yacsloader/samples/pmml_tann_tlr_exportFunctionPMML.pmml create mode 100644 src/yacsloader/samples/pmml_tlr_exportFunctionPMML.pmml create mode 100644 src/yacsloader/samples/schemaANN2.xml create mode 100644 src/yacsloader/samples/schemaANNLR2.xml create mode 100644 src/yacsloader/samples/schemaLR2.xml create mode 100644 src/yacsloader/samples/schemaPmmlDoesNotExist.xml diff --git a/src/pmml/CMakeLists.txt b/src/pmml/CMakeLists.txt new file mode 100755 index 000000000..b94cb263a --- /dev/null +++ b/src/pmml/CMakeLists.txt @@ -0,0 +1,146 @@ +# Copyright (C) 2012-2013 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +# +# TODO : URANIE AND WIN32 : to compile on Windows, user uranietm +# +# To adapt when YACS will be available on Windows +# +# cmake options are : +# +# cmake +# -DURANIE=ON +# -DSALOME_BUILD_TESTS=ON +# -DSALOME_YACS_USE_SWIG=OFF +# -DCMAKE_VERBOSE_MAKEFILE=ON +# -DSALOME_CMAKE_DEBUG=ON +# -DSALOME_BUILD_DOC:BOOL=FALSE +# -G"NMake Makefiles JOM" +# -DCMAKE_INSTALL_PREFIX= +# +# + +IF(URANIE AND WIN32) + CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8 FATAL_ERROR) + SET(SALOME_INSTALL_CMAKE_LOCAL adm/cmake CACHE PATH + "Install path: local SALOME CMake files") + ENABLE_TESTING() +ENDIF(URANIE AND WIN32) + +IF(SALOME_BUILD_DOC) + FIND_PACKAGE(SalomeDoxygen) + FIND_PACKAGE(SalomeGraphviz) + FIND_PACKAGE(SalomeSphinx) + SALOME_LOG_OPTIONAL_PACKAGE(Doxygen SALOME_BUILD_DOC) + SALOME_LOG_OPTIONAL_PACKAGE(Graphviz SALOME_BUILD_DOC) + SALOME_LOG_OPTIONAL_PACKAGE(Sphinx SALOME_BUILD_DOC) +ENDIF(SALOME_BUILD_DOC) + +IF(SALOME_YACS_USE_SWIG) + FIND_PACKAGE(SalomePython) + FIND_PACKAGE(SalomeSWIG) + SALOME_LOG_OPTIONAL_PACKAGE(Python SALOME_YACS_USE_SWIG) + SALOME_LOG_OPTIONAL_PACKAGE(SWIG SALOME_YACS_USE_SWIG) +ENDIF(SALOME_YACS_USE_SWIG) + +# Directories +# +# Directories have to be given after prerequisites (to be able to use +# Python version string for example).adm_local +# =========== +SET(SALOME_INSTALL_BINS bin/salome CACHE PATH "Install path: SALOME binaries") +SET(SALOME_INSTALL_LIBS lib/salome CACHE PATH "Install path: SALOME libs") +SET(SALOME_INSTALL_HEADERS include/salome CACHE PATH "Install path: SALOME headers") + +IF(SALOME_YACS_USE_SWIG) + SET(_pydir lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages) + SET(SALOME_INSTALL_PYTHON ${_pydir}/salome CACHE PATH "Install path: SALOME Python stuff") + SET(SALOME_INSTALL_PYTHON_SHARED ${SALOME_INSTALL_PYTHON}/shared_modules CACHE PATH + "Install path: SALOME Python shared modules") +ENDIF(SALOME_YACS_USE_SWIG) + +SET(SALOME_INSTALL_RES share/salome/resources CACHE PATH "Install path: SALOME resources") +SET(SALOME_PMML_INSTALL_RES_DATA "${SALOME_INSTALL_RES}/pmml" CACHE PATH "Install path: SALOME PMML specific data") + +# Sources +# ======== +IF(WIN32) + ADD_DEFINITIONS("-D_USE_MATH_DEFINES") +ENDIF(WIN32) + +IF(URANIE AND WIN32) + FIND_PACKAGE(LibXml2 REQUIRED) + LINK_DIRECTORIES( ${LIBXML2_LIBRARIES} ) + INCLUDE_DIRECTORIES( ${LIBXML2_INCLUDE_DIR} ) +ELSE(URANIE AND WIN32) + FIND_PACKAGE(SalomeLibXml2 REQUIRED) +ENDIF(URANIE AND WIN32) + +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_BINARY_DIR}/.. + ${LIBXML2_INCLUDE_DIR} + ) + +SET(pmml_SOURCES + PMMLlib.cxx + ) + +ADD_SUBDIRECTORY(resources) + +ADD_LIBRARY(pmmlLib SHARED ${pmml_SOURCES}) +TARGET_LINK_LIBRARIES(pmmlLib ${LIBXML2_LIBRARIES} ) +INSTALL(TARGETS pmmlLib EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_LIBS}) + +FILE(GLOB pmml_HEADERS_HXX "${CMAKE_CURRENT_SOURCE_DIR}/*.hxx") +FILE(GLOB pmml_HEADERS_TXX "${CMAKE_CURRENT_SOURCE_DIR}/*.txx") +INSTALL(FILES ${pmml_HEADERS_HXX} ${pmml_HEADERS_TXX} DESTINATION ${SALOME_INSTALL_HEADERS}) + +# To allow usage as SWIG dependencies: +IF (NOT URANIE) + SET(pmml_HEADERS_HXX PARENT_SCOPE) + SET(pmml_HEADERS_TXX PARENT_SCOPE) +ENDIF (NOT URANIE) + + + +IF(SALOME_BUILD_TESTS) + ADD_SUBDIRECTORY(Test) +ENDIF(SALOME_BUILD_TESTS) + + +IF(SALOME_YACS_USE_SWIG) + ADD_SUBDIRECTORY(pmml_swig) +ENDIF(SALOME_YACS_USE_SWIG) + + +IF(SALOME_BUILD_DOC) + ADD_SUBDIRECTORY(doc) +ENDIF(SALOME_BUILD_DOC) + +# Configuration export +# ==================== + +# - in the install tree: +# Get the relative path of the include directory so +# we can register it in the generated configuration files: +SET(CONF_INCLUDE_DIRS "${CMAKE_INSTALL_PREFIX}/${INSTALL_INCLUDE_DIR}") + +# Install the export set for use with the install-tree +INSTALL(EXPORT ${PROJECT_NAME}TargetGroup DESTINATION "${SALOME_INSTALL_CMAKE_LOCAL}" + FILE ${PROJECT_NAME}Targets.cmake) diff --git a/src/pmml/PMMLlib.cxx b/src/pmml/PMMLlib.cxx new file mode 100755 index 000000000..591800d53 --- /dev/null +++ b/src/pmml/PMMLlib.cxx @@ -0,0 +1,2916 @@ +////////////////////////////////////////////////////////////// +// Copyright (C) 2013 CEA/DEN +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation, either version 3 of the License, or any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +////////////////////////////////////////////////////////////// +/*! + \file PMMLlib.cxx + \author Incka + \date Wed Nov 20 11:04:17 2013 + + \brief Implémentation de la classe PMMLlib + + */ + +// includes Salomé +#include "PMMLlib.hxx" + +// includes C +#include + +// includes C++ +#include +#include +#include +#include + +using namespace std; + +namespace PMMLlib +{ + +//************************************************************** +// * +// * +// * +// méthodes communes à tous les types de modèles * +// * +// * +// * +//************************************************************** + +/** + * Constructor to read a PMML file. + * @param file Name of the PMML file to read + * @param log Flag to print logs or not + */ +PMMLlib::PMMLlib(std::string file,bool log) : + _log(log), + _pmmlFile(file), + _doc(NULL), + _rootNode(NULL), + _currentNode(NULL), + _nbModels(0), + _currentModelName(""), + _currentModelType(kUNDEFINED) +{ + try + { + xmlKeepBlanksDefault(0); + xmlInitParser(); + _doc = xmlParseFile(_pmmlFile.c_str()); + if ( _doc != NULL ) + { + _rootNode = xmlDocGetRootElement(_doc); + CountModels(); + } + else + throw string("Unable to read PMML file."); + } + catch ( std::string msg ) + { + std::cerr << msg; + xmlFreeDoc(_doc); + xmlCleanupParser(); + throw; + } +} + +/** + * Constructor to create a PMML file. + * @brief This constructor is mandatory for Swig because it can be used with no parameters. + * @param log Flag to print logs or not + */ +PMMLlib::PMMLlib(bool log): + _log(log), + _pmmlFile(""), + _doc(NULL), + _rootNode(NULL), + _currentNode(NULL), + _nbModels(0), + _currentModelName(""), + _currentModelType(kUNDEFINED) +{ + SetRootNode(); +} + +/** + * Destructor of the class. + */ +PMMLlib::~PMMLlib() +{ + if (_doc) + xmlFreeDoc(_doc); + xmlCleanupParser(); + if ( _log ) + cout << "~PMMLlib" << endl; +} + +/** + * Set the current model and its type. + * @param modelName Name of the model to load (ie content of 'modelName' attribute) + * @param type Type of PMML to read: one of kANN or kLR + */ +void PMMLlib::SetCurrentModel(std::string modelName, + PMMLType type) +{ + _currentModelName = modelName; + _currentModelType = type; + switch(type) + { + case kANN: + _currentModelNode = GetNeuralNetPtr(modelName); + break; + case kLR: + _currentModelNode = GetRegressionPtr(modelName); + break; + default: + throw string("Unknown PMML type."); + break; + } + if ( _currentModelNode == NULL ) + throw string("Model not found."); +} + +/** + * Set the current model and its type. + * @brief Throw an exception if there is no model or more than one model with name "modelName" in the PMML file + * @param modelName Name of the model to load (ie content of 'modelName' attribute) + */ +void PMMLlib::SetCurrentModel(std::string modelName) +{ + if (_rootNode == NULL) + throw string("No PMML file set."); + xmlNodePtr node = NULL; + int nC = 0; + node = _rootNode->children; + while (node) + { + string nodeModelName = _getProp(node, string("modelName")); + if ( nodeModelName == modelName ) + { + nC++; + _currentModelNode = node; + _currentModelName = modelName; + _currentModelType = GetCurrentModelType(); + } + node = node->next; + } + if ( nC != 1 ) + { + std::ostringstream oss; + oss << nC; + string msg = "SetCurrentModel(modelName) : found " + oss.str() + " model(s) in PMML file.\n"; + msg += "Use SetCurrentModel(modelName,type)."; + throw msg; + } +} + +/** + * Set the current model and its type. + * @brief Throw an exception if no model is found or if there are more than one model in the PMLL file + */ +void PMMLlib::SetCurrentModel() +{ + int nC = _nbModels; + if ( nC != 1 ) + { + std::ostringstream oss; + oss << nC; + string msg = "SetCurrentModel() : found " + oss.str() + " model(s) in PMML file.\n"; + msg += "Use SetCurrentModel(modelName) or SetCurrentModel(modelName,type)."; + throw msg; + } + _currentModelNode = GetChildByName(_rootNode,"NeuralNetwork"); + _currentModelType = kANN; + if (_currentModelNode == NULL) + { + _currentModelNode = GetChildByName(_rootNode,"RegressionModel"); + _currentModelType = kLR; + } + if (_currentModelNode == NULL) + { + string msg("Couldn't get node in SetCurrentModel()."); + throw msg; + } + _currentModelName = _getProp(_currentModelNode, string("modelName")); +} + +/** + * Make the string used by PMMLlib::printLog. + * @return The log + */ +std::string PMMLlib::makeLog() const +{ + ostringstream out; + out << "**\n**** Display of PMMLlib ****" << endl; + out << " ** _pmmlFile[" << _pmmlFile << "]" << endl; + out << " ** _log[" << (_log?1:0) << "]" << endl; + out << "**\n**** End of display of PMMLlib ****" << endl; + return out.str(); +} + +/** + * Print some information about the current PMML object. + */ +void PMMLlib::printLog() const +{ + string log = makeLog(); + cout << log << endl; +} + +/** + * Set the root node in the tree: + * @code + * + * @endcode + */ +void PMMLlib::SetRootNode() +{ + xmlChar * xs = _stringToXmlChar("1.0"); + _doc = xmlNewDoc(xs); + xmlFree(xs); + + xmlChar *xp = _stringToXmlChar("PMML"); + _rootNode = xmlNewNode(0, xp); + xmlFree(xp); + + xmlNewProp(_rootNode, (const xmlChar*)"xmlns", (const xmlChar*)"http://www.dmg.org/PMML-4_1"); + xmlNewProp(_rootNode, (const xmlChar*)"version", (const xmlChar*)"4.1"); + + xmlDocSetRootElement(_doc, _rootNode); +} + + +/** + * Set the header node in the tree. + * @param copyright Copyright of the PMML file + * @param description Description of the model + * @param appName Name of the application that produced the file + * @param appVersion Version of the application that produced the file + * @param annotation Some annotation + */ +void PMMLlib::SetHeader(std::string copyright, + std::string description, + std::string appName, + std::string appVersion, + std::string annotation) +{ + xmlNodePtr headerNode = xmlNewChild(_rootNode, 0, (const xmlChar*)"Header", 0); + xmlNewProp(headerNode, (const xmlChar*)"copyright", (const xmlChar*)(copyright.c_str())); + xmlNewProp(headerNode, (const xmlChar*)"description", (const xmlChar*)(description.c_str())); + + xmlNodePtr appNode = xmlNewChild(headerNode, 0, (const xmlChar*)"Application", 0); + xmlNewProp(appNode, (const xmlChar*)"name", (const xmlChar*)(appName.c_str())); + xmlNewProp(appNode, (const xmlChar*)"version", (const xmlChar*)(appVersion.c_str())); + + xmlNewChild(headerNode, 0, (const xmlChar*)"Annotation", (const xmlChar*)(annotation.c_str())); +} + +/** + * Add the MiningSchema node. + * @brief Common to all models. + * @param name Value of property "name". + * @param usageType Value of property "usageType". + */ +void PMMLlib::AddMiningSchema(std::string name, + std::string usageType) +{ + xmlNodePtr netNode = _currentModelNode; + + // if 'MiningSchema' node does not exist, create it + xmlNodePtr miningSchemaNode = GetChildByName(netNode, "MiningSchema"); + if(!miningSchemaNode) + { + miningSchemaNode = xmlNewChild(netNode, 0, (const xmlChar*)"MiningSchema", 0); + } + + // then append the node + xmlNodePtr miningFieldNode = xmlNewChild(miningSchemaNode, 0, (const xmlChar*)"MiningField", 0); + xmlNewProp(miningFieldNode, (const xmlChar*)"name", (const xmlChar*)(name.c_str()) ); + xmlNewProp(miningFieldNode, (const xmlChar*)"usageType", (const xmlChar*)(usageType.c_str()) ); +} + +/** + * Get the child of a node from the name of this node + * @param node Start node for the research + * @param nodeName Name of the node to find + * @return Pointer to the node found + */ +xmlNodePtr PMMLlib::GetChildByName(xmlNodePtr node, + std::string nodeName) +{ + if ( node == NULL ) + return node; + + xmlNodePtr childNode = node->children; + if ( childNode == NULL ) + return childNode; + + const xmlChar* name = childNode->name; + string strName(""); + if ( name != NULL ) + strName = _xmlCharToString(name); + + while( (childNode != NULL) && (strName != nodeName) ) + { + childNode = childNode->next; + if ( childNode == NULL ) + return childNode; + name = childNode->name; + if ( name != NULL ) + strName = _xmlCharToString(name); + } + return childNode; +} + +/** + * Count the tags of all types of models (NeuralNetwork and RegressionModel). + * @return Number of models + */ +void PMMLlib::CountModels() +{ + int nCount = 0; + nCount = CountNeuralNetModels() + CountRegressionModels(); + if ( _log) + cout << " ** End Of Count Models nCount[" << nCount << "]" << endl; + _nbModels = nCount ; +} + +/** + * Count NeuralNetwork models tags in the PMML file. + * @return Number of models + */ +int PMMLlib::CountNeuralNetModels() +{ + int nCount = 0; + xmlNodePtr ptr = GetChildByName(_rootNode,"NeuralNetwork"); + // Count the models + while (ptr != NULL && _xmlCharToString(ptr->name) == "NeuralNetwork") + { + nCount++; + if (_log) + cout << " ** nCount[" << nCount << "]" << endl; + ptr = ptr->next; + } + if ( _log) + cout << " ** End Of CountNetworks nCount[" << nCount << "]" << endl; + return nCount; +} + +/** + * Count RegressionModel models tags in the PMML file. + * @return Number of models + */ +int PMMLlib::CountRegressionModels() +{ + int nCount = 0; + xmlNodePtr ptr = GetChildByName(_rootNode,"RegressionModel"); + // Count the models + while (ptr != NULL && _xmlCharToString(ptr->name) == "RegressionModel") + { + nCount++; + if (_log) + cout << " ** nCount[" << nCount << "]" << endl; + ptr = ptr->next; + } + if ( _log) + cout << " ** End Of CountRegressions nCount[" << nCount << "]" << endl; + return nCount; +} + +/** + * Get the number of models + * @return Number of models + */ +int PMMLlib::GetModelsNb() +{ + return _nbModels; +} + +/** + * Get the name of the XML node of a given model + * @param node Model node + * @return value of attribute "modelName" of the model node + */ +std::string PMMLlib::GetModelName(xmlNodePtr node) +{ + string name(""); + name = _getProp(node, string("modelName") ); + return name; +} + +/** + * Get a pointer to the index-th node named name + * @param index Index of the node to search + * @param name Name of the node + * @return Pointer to the node found + */ +xmlNodePtr PMMLlib::GetPtr(int index, + std::string name) +{ + xmlNodePtr node = NULL; + + if (_doc != NULL) + { + _rootNode = xmlDocGetRootElement(_doc); + node = GetChildByName(_rootNode, name); + + int i=0; + while ((i != index) && (node != NULL)) + { + node = node->next; + i++; + } + } + return node; +} + +/** + * Get a pointer to the node named name whose 'modelName' attribute is ann_name + * @param modelName Model name of the node to search + * @param nodeName Name of the node + * @return Pointer to the node found + */ +xmlNodePtr PMMLlib::GetPtr(std::string myModelName, + std::string nodeName) +{ + xmlNodePtr node = NULL; + if (_doc != NULL) + { + node = GetChildByName(_rootNode, nodeName); + if( node ) + { + string modelName = _getProp(node, string("modelName")); + + while ( (node != NULL) && modelName != myModelName ) + { + node = node->next; + if( node ) + { + modelName = _getProp(node, string("modelName")); + } + } + } + } + return node; +} + +/** + * Get the tag of the current model. + * @return Current model tag + */ +std::string PMMLlib::GetTypeString() +{ + string name = ""; + switch(_currentModelType) + { + case kANN: + name = "NeuralNetwork"; + break; + case kLR: + name = "RegressionModel"; + break; + default: + throw string("Unknown PMML type."); + break; + } + return name; +} + +/** + * Get the current model type. + * @brief type is kUNDEFINED if no model is set or if model type is not handled + * @return the type + */ +PMMLType PMMLlib::GetCurrentModelType() +{ + PMMLType type = kUNDEFINED ; + if ( ! _currentModelNode ) + return type; + string name = _xmlCharToString(_currentModelNode->name); + if ( name == "NeuralNetwork" ) + type = kANN; + else if ( name == "RegressionModel" ) + type = kLR; + return type; +} + +/** + * Get the current model name. + * @brief name is "" if no model is set + * @return the type + */ +std::string PMMLlib::GetCurrentModelName() +{ + if ( ! _currentModelNode ) + return string(""); + string name = _getProp(_currentModelNode, string("modelName")); + return name; +} + +/** + * Unlink the current model node. + */ +void PMMLlib::UnlinkNode() +{ + xmlNodePtr ptr = _currentModelNode ; + xmlUnlinkNode( ptr ); + xmlFreeNode( ptr ); +} + +/** + * Make a backup of the current model node. + */ +void PMMLlib::BackupNode() +{ + // Node name depending of PMML type + string name = GetTypeString(); + // Find the last save index number + int nCrtIndex = 0; + stringstream ss; + ss << _currentModelName << "_" << nCrtIndex; + xmlNodePtr ptr = GetPtr(ss.str(), name); + while( ptr ) + { + nCrtIndex++; + if (_log) + cout << " ** nCrtIndex[" << nCrtIndex << "]" << endl; + + ss.str(""); + ss << _currentModelName << "_" << nCrtIndex; + ptr = GetPtr(ss.str(), name); + } + if(_log) + cout << " *** Node \"" << _currentModelName << "\" found, then backup it with index [" << nCrtIndex << "]" << endl; + // Rename model + xmlUnsetProp(_currentModelNode, (const xmlChar*)"modelName"); + xmlNewProp(_currentModelNode, (const xmlChar*)"modelName", (const xmlChar*)(ss.str().c_str())); +} + +/** + * Save the XML tree in the PMML file + */ +void PMMLlib::Write() +{ + // Enregistrement de l'arbre DOM dans le fichier pmml + Write(_pmmlFile); + // Mise à jour du nombre de modèles + CountModels(); +} + +/** + * Save the XML tree in a given file + * @param Name of the file + */ +void PMMLlib::Write(std::string file) +{ + // Enregistrement de l'arbre DOM sous forme de fichier pmml + int ret = xmlSaveFormatFile( file.c_str(), _doc, 1); + if ( ret == -1 ) + { + std::string msg(" *** Error :: unable to write the PMML file \"" + file + "\"") ; + cout << msg << endl; + throw msg; + } + if ( _log ) + cout << " *** Write the PMML file \"" << file <<"\"" << endl; +} + +/** + * Export the current model as a function in a Cpp file. + * @param file Name of the file + * @param functionName Name of the function + * @param header Header of the function + */ +void PMMLlib::ExportCpp(std::string file, + std::string functionName, + std::string header) +{ + if ( _currentModelType == kANN ) + ExportNeuralNetworkCpp(file,functionName, header); + else if ( _currentModelType == kLR ) + { + ExportLinearRegressionCpp(file, functionName, header); + } + else + throw string("ExportCpp : PMML type not handled."); +} + +/** + * Export the current model as a function in a Fortran file. + * @param file Name of the file + * @param functionName Name of the function + * @param header Header of the function + */ +void PMMLlib::ExportFortran(std::string file, + std::string functionName, + std::string header) +{ + if ( _currentModelType == kANN ) + ExportNeuralNetworkFortran(file,functionName, header); + else if ( _currentModelType == kLR ) + ExportLinearRegressionFortran(file,functionName, header); + else + throw string("ExportFortran : PMML type not handled."); +} + +/** + * Export the current model as a function in a Python file. + * @param file Name of the file + * @param functionName Name of the function + * @param header Header of the function + */ +void PMMLlib::ExportPython(std::string file, + std::string functionName, + std::string header) +{ + if ( _currentModelType == kANN ) + ExportNeuralNetworkPython(file,functionName, header); + else if ( _currentModelType == kLR ) + ExportLinearRegressionPython(file,functionName, header); + else + throw string("ExportPython : PMML type not handled."); +} + +/** + * Export the current model as a function in a Python string. + * @param file Name of the file + * @param functionName Name of the function + * @param header Header of the function + * @return Function as a string + */ +std::string PMMLlib::ExportPyStr(std::string functionName, + std::string header) +{ + if ( _currentModelType == kANN ) + return ExportNeuralNetworkPyStr(functionName, header); + else if ( _currentModelType == kLR ) + return ExportLinearRegressionPyStr(functionName, header); + else + throw string("ExportPyStr : PMML type not handled."); +} + +/*! + * Conversion from a libxml2 string (xmlChar *) to a standard C++ string. + * \param xs a constant libxml string. + * \return a C++ std::string (contains the same text as xs). + */ +std::string PMMLlib::_xmlCharToString(const xmlChar *xs) const +{ + size_t i, L = xmlStrlen(xs); + std::string s; + s.resize(L); + for (i=0; *xs; s[i++] = *xs++); + return s; +} + +/*! + * Conversion from a a standard C++ string to a libxml2 string (xmlChar *). + * \param s Constant C++ std::string (contains the same text as xs) + * \return Constant libxml string. + */ +xmlChar * PMMLlib::_stringToXmlChar(const std::string &s) const +{ + return xmlCharStrdup(s.c_str()); +} + +/*! + * Get the value of a node property. + * \param node Tag + * \param prop Property + * \return Constant libxml string. + */ +std::string PMMLlib::_getProp(const xmlNodePtr node, + std::string const & prop ) const +{ + std::string name(""); + if (_doc != NULL) + { + xmlChar *xp = _stringToXmlChar(prop); + xmlChar * attr ; + attr = xmlGetProp(node, xp ); + if ( attr ) + { + name = _xmlCharToString(attr ); + xmlFree(attr); + } + xmlFree(xp); + } + return name; +} + +//************************************************************** +// * +// * +// * +// méthodes propres au NeuralNetwork * +// * +// * +// * +//************************************************************** + +/*! + * Check if the current model type is kANN. + * \brief Called in all methods specific to the NeuralNetwork model. + * \brief Throw an exception if the model type is not kANN. + */ +void PMMLlib::CheckNeuralNetwork() +{ + if ( _currentModelType != kANN ) + throw string("Use this method with NeuralNetwork models."); +} + +/** + * Get the XML node of a given network from the index + * @param index Index of the neural network + * @return Pointer to the XML node + */ +xmlNodePtr PMMLlib::GetNeuralNetPtr(int index) +{ + return GetPtr(index, GetTypeString() ); +} + +/** + * Get the XML node of a given network model + * @param name Name of the neural network + * @return Pointer to the XML node + */ +xmlNodePtr PMMLlib::GetNeuralNetPtr(std::string name) +{ + return GetPtr(name, GetTypeString() ); +} + +/** + * Read the structure of the network + * @brief Specific to NeuralNetwork + * @return Structure read + */ +std::string PMMLlib::ReadNetworkStructure() +{ + CheckNeuralNetwork(); + + string structure(""); + // Treatment of the input + xmlNodePtr inputNodes = GetChildByName(_currentModelNode,"NeuralInputs"); + if ( inputNodes != NULL ) + { + xmlNodePtr inputNode = GetChildByName(inputNodes,"NeuralInput"); + if ( inputNode != NULL ) + { + while (inputNode != NULL) + { + xmlNodePtr child = GetChildByName(inputNode,"DerivedField"); + if ( child != NULL ) + { + xmlNodePtr fieldName = child->children; // NormContinuous + if ( fieldName != NULL ) + { + string field = _getProp(fieldName, string("field")); + structure += field; + structure += ":"; + } + } + inputNode = inputNode->next; + } + // Delete the last comma + structure.erase(structure.size()-1); + } + } + // Intermediary layers + xmlNodePtr node_layer = GetChildByName(_currentModelNode,"NeuralLayer"); + if ( node_layer != NULL ) + { + string name = string((const char*)(node_layer->name)); + structure += ","; + + while ( node_layer != NULL && + (string((const char*)(node_layer->name)) == "NeuralLayer") && + node_layer->next != NULL && + (string((const char*)(node_layer->next->name)) != "NeuralOutputs") ) + { + // Get the number of neurons of the current layer + string nbneurons = _getProp(node_layer, string("numberOfNeurons")); + structure += nbneurons; + structure += ","; + node_layer = node_layer->next; + } + } + // Output layers + xmlNodePtr node_outputs = GetChildByName(_currentModelNode,"NeuralOutputs"); + if ( node_outputs != NULL ) + { + xmlNodePtr node_output = GetChildByName(node_outputs,"NeuralOutput"); + if ( node_output != NULL ) + { + while (node_output != NULL) + { + // Get the input of the current layer + xmlNodePtr child = GetChildByName(node_output,"DerivedField"); + if ( child != NULL ) + { + xmlNodePtr fieldName = child->children; // NormContinuous + if ( fieldName != NULL ) + { + if (string((const char*)(fieldName->name)) == "NormContinuous") + structure += "@"; + + string field = _getProp(fieldName, string("field")); + structure += field; + structure += ":"; + } + } + node_output = node_output->next; + } + // Delete the last comma + structure.erase(structure.size()-1); + } + } + return structure; +} + +/** + * Get the number of inputs, ie the number of NeuralInputs nodes. + * @brief Specific to NeuralNetwork + * @return Number of input nodes + */ +int PMMLlib::GetNbInputs() +{ + CheckNeuralNetwork(); + + int nb=0; + xmlNodePtr node_inputs = GetChildByName(_currentModelNode,"NeuralInputs"); + if ( node_inputs == NULL ) + return nb; + + node_inputs = node_inputs->children; + while (node_inputs != NULL) + { + nb++; + node_inputs = node_inputs->next; + } + + return nb; +} + +/** + * Recover the number of outputs + * @brief Specific to NeuralNetwork + * @return Number of outputs + */ +int PMMLlib::GetNbOutputs() +{ + CheckNeuralNetwork(); + + int nb=0; + xmlNodePtr node_outputs = GetChildByName(_currentModelNode,"NeuralOutputs"); + if ( node_outputs == NULL ) + return nb; + + node_outputs = node_outputs->children; + + while (node_outputs != NULL) + { + nb++; + node_outputs = node_outputs->next; + } + + return nb; +} + +/** + * Recovery of the name of an input in the current model. + * @brief Specific to NeuralNetwork + * @param index Index of the input + * @return Name of the input + */ +std::string PMMLlib::GetNameInput(int index) +{ + CheckNeuralNetwork(); + + string name(""); + xmlNodePtr node_inputs = GetChildByName(_currentModelNode,"NeuralInputs"); + if ( node_inputs == NULL ) + return name; + + node_inputs = node_inputs->children; + if ( node_inputs == NULL ) + return name; + + for(int i = 0;inext; + if ( node_inputs == NULL ) + return name; + } + + node_inputs = node_inputs->children; + if ( node_inputs == NULL ) + return name; + + node_inputs = node_inputs->children; + if ( node_inputs == NULL ) + return name; + + name = _getProp(node_inputs, string("field")); + + return name; +} + +/** + * Get the name of an output in the current model. + * @brief Specific to NeuralNetwork + * @param index Index of the output + * @return Name of the output + */ +std::string PMMLlib::GetNameOutput(int index) +{ + CheckNeuralNetwork(); + + string name(""); + xmlNodePtr node_outputs = GetChildByName(_currentModelNode,"NeuralOutputs"); + if ( node_outputs == NULL ) + return name; + node_outputs = node_outputs->children; + if ( node_outputs == NULL ) + return name; + for(int i = 0;inext; + if ( node_outputs == NULL ) + return name; + } + + node_outputs = node_outputs->children; + if ( node_outputs == NULL ) + return name; + node_outputs = node_outputs->children; + if ( node_outputs == NULL ) + return name; + + name = _getProp(node_outputs, string("field") ); + + return name; +} + +/** + * Get the normalization type of the current model + * @brief Specific to NeuralNetwork + * @return Normalization type of the neural network + */ +int PMMLlib::GetNormalizationType() +{ + CheckNeuralNetwork(); + + xmlNodePtr node_inputs = GetChildByName(_currentModelNode,"NeuralInputs"); + node_inputs = GetChildByName(node_inputs,"NeuralInput"); + xmlNodePtr nodeTmp = GetChildByName(node_inputs,"DerivedField"); + xmlNodePtr node_field = nodeTmp->children; + xmlNodePtr node_linearnorm; + string str_tmp; + double dorig1, dnorm1; + double dorig2, dnorm2; + if (string((const char*)(node_field->name)) == "NormContinuous") + { + // Get mean and standard deviation + node_linearnorm = node_field->children; + str_tmp = _getProp(node_linearnorm, string("orig")); + dorig1 = atof(str_tmp.c_str()); + str_tmp = _getProp(node_linearnorm, string("norm")); + dnorm1 = atof(str_tmp.c_str()); + node_linearnorm = node_linearnorm->next; + str_tmp = _getProp(node_linearnorm, string("orig")); + dorig2 = atof(str_tmp.c_str()); + str_tmp = _getProp(node_linearnorm, string("norm")); + dnorm2 = atof(str_tmp.c_str()); + if ( dnorm1 * dnorm2 < -0.5 ) + { // case of kMinusOneOne + return 0; + } + else + { // case of kCR, kZeroOne + return 1; + } + } + string msg("Unable to retrieve the normalization type."); + throw msg; +} + +/** + * Get the input parameters on the normalization + * @brief Specific to NeuralNetwork + * @param node_ann Neural network node + * @param index Index of the input + * @param[out] dnorm Array that contains the mean and the standard deviation + */ +void PMMLlib::GetNormalisationInput(int index, + double *dnorm) +{ + CheckNeuralNetwork(); + dnorm[0] = 0.0; + dnorm[1] = 0.0; + xmlNodePtr node_inputs = GetChildByName(_currentModelNode,"NeuralInputs"); + if ( node_inputs == NULL ) + return ; + node_inputs = GetChildByName(node_inputs,"NeuralInput"); + if ( node_inputs == NULL ) + return ; + // Positionnement sur la bonne entree + for(int i=0;inext; + if ( node_inputs == NULL ) + return ; + } + xmlNodePtr tmpNode = GetChildByName(node_inputs,"DerivedField"); + if ( tmpNode == NULL ) + return ; + xmlNodePtr node_field = GetChildByName(tmpNode,"NormContinuous"); + if ( node_field == NULL ) + return ; + if (string((const char*)(node_field->name)) == "NormContinuous") + { + //Get mean and standard deviation + string str_tmp; + xmlNodePtr node_linearnorm = node_field->children; + str_tmp = _getProp(node_linearnorm, string("orig")); + double dorig1 = atof(str_tmp.c_str()); + str_tmp = _getProp(node_linearnorm, string("norm")); + double dnorm1 = atof(str_tmp.c_str()); + node_linearnorm = node_linearnorm->next; + str_tmp = _getProp(node_linearnorm, string("orig")); + double dorig2 = atof(str_tmp.c_str()); + str_tmp = _getProp(node_linearnorm, string("norm")); + double dnorm2 = atof(str_tmp.c_str()); + if ( dnorm1 * dnorm2 < -0.5 ) // <=> GetNormalizationType == 0 + { + // case of kMinusOneOne + dnorm[0] = dorig1; + dnorm[1] = dorig2; + } + else // <=> GetNormalizationType == 1 + { + // case of kCR, kZeroOne + dnorm[0] = dorig2; + dnorm[1] = -1.0 * dnorm1 * dorig2; //dorig2 / dnorm1; + } + } +} + +/** + * Get the parameters on the normalization of an output for the current model. + * @brief Specific to NeuralNetwork + * @param index Output index + * @param[out] dnorm Array that contains the mean and the standard deviation + */ +void PMMLlib::GetNormalisationOutput(int index, + double *dnorm) +{ + CheckNeuralNetwork(); + dnorm[0] = 0.0; + dnorm[1] = 0.0; + + xmlNodePtr node_outputs = GetChildByName(_currentModelNode,"NeuralOutputs"); + if ( node_outputs == NULL ) + return ; + node_outputs = GetChildByName(node_outputs,"NeuralOutput"); + if ( node_outputs == NULL ) + return ; + // Positionnement sur la bonne sortie + for(int i=0;i< index;i++) + { + node_outputs = node_outputs->next; + if ( node_outputs == NULL ) + return ; + } + xmlNodePtr tmpNode = GetChildByName(node_outputs,"DerivedField"); + if ( tmpNode == NULL ) + return ; + xmlNodePtr node_field = GetChildByName(tmpNode,"NormContinuous"); + if ( node_field == NULL ) + return ; + + if (string((const char*)(node_field->name)) == "NormContinuous") + { + // Recuperation de la moyenne et de l'ecart type + string str_tmp; + xmlNodePtr node_linearnorm = node_field->children; + str_tmp = _getProp(node_linearnorm, string("orig")); + double dorig1 = atof(str_tmp.c_str()); + str_tmp = _getProp(node_linearnorm, string("norm")); + double dnorm1 = atof(str_tmp.c_str()); + node_linearnorm = node_linearnorm->next; + str_tmp = _getProp(node_linearnorm,string("orig")); + double dorig2 = atof(str_tmp.c_str()); + str_tmp = _getProp(node_linearnorm, string("norm")); + double dnorm2 = atof(str_tmp.c_str()); + if ( dnorm1 * dnorm2 < -0.5 ) + { + // case of kMinusOneOne + dnorm[0] = dorig1; + dnorm[1] = dorig2; + } + else + { + // case of kCR, kZeroOne + dnorm[0] = dorig2; + dnorm[1] = -1.0 * dorig2 * dnorm1; //-1.0 * dorig2 / dnorm1; + } + } +} + +/** + * Get the number of hidden layers + * @brief Specific to NeuralNetwork + * @return Number of hidden layers + */ +int PMMLlib::GetNbHiddenLayers() +{ + CheckNeuralNetwork(); + + int nb_layers = 0; + xmlNodePtr node_layers = GetChildByName(_currentModelNode,"NeuralLayer"); + if ( node_layers == NULL ) + return nb_layers; + + while (string((const char*)(node_layers->name)) == "NeuralLayer") + { + nb_layers++; + node_layers = node_layers->next; + if ( node_layers == NULL ) + return nb_layers; + } + return nb_layers; +} + +/** + * Get the total number of layers + * @return Total number of layers + */ +int PMMLlib::GetNbLayers() +{ + return (GetNbHiddenLayers() + 2); +} + +/** + * Get the number of neurons at a given layer + * @param index Index of the layer + * @return Number of neurons at given layer + */ +int PMMLlib::GetNbNeuronsAtLayer(int index) +{ + CheckNeuralNetwork(); + + int nb_neurons = 0; + xmlNodePtr node_layers = GetChildByName(_currentModelNode,"NeuralLayer"); + if ( node_layers == NULL ) + return nb_neurons; + + // Positionnement à la bonne couche + for(int i=0;inext; + if ( node_layers == NULL ) + return nb_neurons; + } + + xmlNodePtr node_neurons = GetChildByName(node_layers,"Neuron"); + while(node_neurons != NULL) + { + nb_neurons++; + node_neurons = node_neurons->next; + } + + return nb_neurons; +} + +/** + * Get the bias of a neuron + * @brief Specific to NeuralNetwork + * @param layer_index Index of the layer to get bias + * @param neu_index Index of the neuron + * @return Bias of the specified neuron + */ +double PMMLlib::GetNeuronBias(int layer_index, + int neu_index) +{ + CheckNeuralNetwork(); + + double bias = 0.; + xmlNodePtr node_layers = GetChildByName(_currentModelNode,"NeuralLayer"); + if ( node_layers == NULL ) + return bias; + // Positionnement a la bonne couche + for(int i=0;inext; + if ( node_layers == NULL ) + return bias; + } + xmlNodePtr node_neurons = GetChildByName(node_layers,"Neuron"); + // Positionnement sur le bon neurone + for(int j=0;jnext; + if ( node_neurons == NULL ) + return bias; + } + string str_tmp = _getProp(node_neurons, string("bias")); + bias = atof(str_tmp.c_str()); + return bias; +} + +/** + * Get the synaptic weight + * @brief Specific to NeuralNetwork + * @param layer_index Index of the layer to get synaptic weight + * @param neu_index Index of the neuron + * @param prec_index Index of the synapse + * @return Synaptic weight + */ +double PMMLlib::GetPrecNeuronSynapse(int layer_index, + int neu_index, + int prec_index) +{ + CheckNeuralNetwork(); + + double weight = 0.; + xmlNodePtr node_layers = GetChildByName(_currentModelNode,"NeuralLayer"); + if ( node_layers == NULL ) + return weight; + // Positionnement a la bonne couche + for(int i=0;inext; + if ( node_layers == NULL ) + return weight; + } + xmlNodePtr node_neurons = GetChildByName(node_layers,"Neuron"); + // Positionnement sur le bon neurone + for(int i=0;inext; + if ( node_neurons == NULL ) + return weight; + } + xmlNodePtr node_con = GetChildByName(node_neurons,"Con"); + // Positionnement sur la bonne synapse + for(int i=0;inext; + if ( node_con == NULL ) + return weight; + } + string str_tmp = _getProp(node_con, string("weight")); + weight = atof(str_tmp.c_str()); + return weight; +} + +/** + * Set the name of the neural network + * @brief Not tested + * @param index Neural network index + * @param name Neural network name to set + */ +// LCOV_EXCL_START +void PMMLlib::SetNeuralNetName(int index, + std::string name) +{ + CheckNeuralNetwork(); + + int i=0; + if (_doc != NULL) + { + xmlNodePtr node_ann = GetChildByName(_rootNode,"NeuralNetwork"); + while ((i != index) && (node_ann != NULL)) + { + node_ann = node_ann->next; + i++; + } + xmlNewProp(node_ann, (const xmlChar*)"modelName", (const xmlChar*)(name.c_str())); + } + xmlSaveFormatFile( string(_pmmlFile+".pmml").c_str(), _doc, 1); +} +// LCOV_EXCL_STOP + +/** + * Add a DataField node to the DataDictionnary node + * @param fieldName Value of property "name" + * @param displayName Value of property "displayName" + * @param optype Value of property "optype" + * @param dataType Value of property "dataType" + * @param closure Value of property "closure" in node Interval + * @param leftMargin Value of property "leftMargin" in node Interval + * @param rightMargin Value of property "rightMargin" in node Interval + * @param interval Flag to add a node Interval (if true) + */ +void PMMLlib::AddDataField(std::string fieldName, + std::string displayName, + std::string optype, + std::string dataType, + std::string closure, + double leftMargin, + double rightMargin, + bool interval) +{ + // if 'DataDictionary' node does not exist, create it + xmlNodePtr dataDictNode = GetChildByName(_rootNode, "DataDictionary"); + if(!dataDictNode) + { + dataDictNode = xmlNewChild(_rootNode, 0, (const xmlChar*)"DataDictionary", 0); + } + + // then append the node + xmlNodePtr dataFieldNode = xmlNewChild(dataDictNode, 0, (const xmlChar*)"DataField", 0); + xmlNewProp(dataFieldNode, (const xmlChar*)"name", (const xmlChar*)(fieldName.c_str()) ); + xmlNewProp(dataFieldNode, (const xmlChar*)"displayName", (const xmlChar*)(displayName.c_str()) ); + xmlNewProp(dataFieldNode, (const xmlChar*)"optype", (const xmlChar*)(optype.c_str()) ); + xmlNewProp(dataFieldNode, (const xmlChar*)"dataType", (const xmlChar*)(dataType.c_str()) ); + + if ( interval ) + { + xmlNodePtr intervalNode = xmlNewChild(dataFieldNode, 0, (const xmlChar*)"Interval", 0); + xmlNewProp(intervalNode, (const xmlChar*)"closure", (const xmlChar*)(closure.c_str()) ); + stringstream ss; + ss << scientific << leftMargin; + xmlNewProp(intervalNode, (const xmlChar*)"leftMargin", (const xmlChar*)(ss.str().c_str()) ); + ss.str(""); + ss << scientific << rightMargin; + xmlNewProp(intervalNode, (const xmlChar*)"rightMargin", (const xmlChar*)(ss.str().c_str()) ); + } +} + +/** + * Add a NeuralNetwork node to the root node + * @brief Specific to NeuralNetwork + * @param modelName Model name + * @param functionName PMMLMiningFunction. One of : kREGRESSION. + */ +void PMMLlib::AddNeuralNetwork(std::string modelName, + PMMLMiningFunction functionName) +{ + _currentModelType = kANN; + _currentModelName = modelName; + + CheckNeuralNetwork(); + + string function; + switch(functionName) + { + case kREGRESSION: + function = "regression"; + break; + } + + xmlNodePtr netNode = xmlNewChild(_rootNode, 0, (const xmlChar*)"NeuralNetwork", 0); + xmlNewProp(netNode, (const xmlChar*)"modelName", (const xmlChar*)(_currentModelName.c_str()) ); + xmlNewProp(netNode, (const xmlChar*)"functionName", (const xmlChar*)(function.c_str()) ); + xmlNewProp(netNode, (const xmlChar*)"numberOfLayers", (const xmlChar*)"0" ); + _currentModelNode = netNode; +} + + /** + * Add a NeuralInput node to the current model. + * @brief Specific to NeuralNetwork + * @param id Id of the input + * @param inputName Name of the input + * @param optype Value of property "optype" + * @param dataType Value of property "dataType" + * @param orig1 Value of the first origin + * @param norm1 Value of the first norm + * @param orig2 Value of the second origin + * @param norm2 Value of the second norm + */ +void PMMLlib::AddNeuralInput(int id, + std::string inputName, + std::string optype, + std::string dataType, + double orig1, double norm1, + double orig2, double norm2) +{ + CheckNeuralNetwork(); + + xmlNodePtr netNode = _currentModelNode; + // if 'NeuralInputs' node does not exist, create it + xmlNodePtr neuralInputsNode = GetChildByName(netNode, "NeuralInputs"); + if(!neuralInputsNode) + { + neuralInputsNode = xmlNewChild(netNode, 0, (const xmlChar*)"NeuralInputs", 0); + xmlNewProp(neuralInputsNode, (const xmlChar*)"numberOfInputs", (const xmlChar*)"0" ); + } + // increment the number of inputs + string numberOfInputsStr = _getProp(neuralInputsNode, string("numberOfInputs")); + int numberOfInputs; + istringstream( numberOfInputsStr ) >> numberOfInputs; + numberOfInputs++; + stringstream ss; + ss << numberOfInputs; + xmlSetProp(neuralInputsNode, (const xmlChar*)"numberOfInputs", (const xmlChar*)(ss.str().c_str()) ); + // then append the node and its children + xmlNodePtr neuralInputNode = xmlNewChild(neuralInputsNode, 0, (const xmlChar*)"NeuralInput", 0); + ss.str(""); ss << id; + xmlNewProp(neuralInputNode, (const xmlChar*)"id", (const xmlChar*)(ss.str().c_str()) ); + + xmlNodePtr derivedFieldNode = xmlNewChild(neuralInputNode, 0, (const xmlChar*)"DerivedField", 0); + xmlNewProp(derivedFieldNode, (const xmlChar*)"optype", (const xmlChar*)(optype.c_str()) ); + xmlNewProp(derivedFieldNode, (const xmlChar*)"dataType", (const xmlChar*)(dataType.c_str()) ); + + xmlNodePtr normcontNode = xmlNewChild(derivedFieldNode, 0, (const xmlChar*)"NormContinuous", 0); + xmlNewProp(normcontNode, (const xmlChar*)"field", (const xmlChar*)(inputName.c_str()) ); + + xmlNodePtr node_linearnorm1 = xmlNewChild(normcontNode, 0, (const xmlChar*)"LinearNorm", 0); + ss.str(""); ss << scientific << orig1; + xmlNewProp(node_linearnorm1, (const xmlChar*)"orig", (const xmlChar*)(ss.str().c_str()) ); + ss.str(""); ss << scientific << norm1; + xmlNewProp(node_linearnorm1, (const xmlChar*)"norm", (const xmlChar*)(ss.str().c_str()) ); + xmlNodePtr node_linearnorm2 = xmlNewChild(normcontNode, 0, (const xmlChar*)"LinearNorm", 0); + ss.str(""); ss << scientific << orig2; + xmlNewProp(node_linearnorm2, (const xmlChar*)"orig", (const xmlChar*)(ss.str().c_str()) ); + ss.str(""); ss << scientific << norm2; + xmlNewProp(node_linearnorm2, (const xmlChar*)"norm", (const xmlChar*)(ss.str().c_str()) ); +} + + /** + * Add a NeuralOutput node to the current model. + * @brief Specific to NeuralNetwork + * @param outputNeuron Id of the output + * @param outputName Name of the output + * @param optype Value of property "optype" + * @param dataType Value of property "dataType" + * @param orig1 Value of the first origin + * @param norm1 Value of the first norm + * @param orig2 Value of the second origin + * @param norm2 Value of the second norm + */ +void PMMLlib::AddNeuralOutput(int outputNeuron, + std::string outputName, + std::string optype, + std::string dataType, + double orig1, double norm1, + double orig2, double norm2) +{ + CheckNeuralNetwork(); + + xmlNodePtr netNode = _currentModelNode; + // if 'NeuralOutputs' node does not exist, create it + xmlNodePtr neuralOutputsNode = GetChildByName(netNode, "NeuralOutputs"); + if(!neuralOutputsNode) + { + neuralOutputsNode = xmlNewChild(netNode, 0, (const xmlChar*)"NeuralOutputs", 0); + xmlNewProp(neuralOutputsNode, (const xmlChar*)"numberOfOutputs", (const xmlChar*)"0" ); + } + // increment the number of inputs + string numberOfOutputsStr = _getProp(neuralOutputsNode, string("numberOfOutputs")); + int numberOfOutputs; + istringstream( numberOfOutputsStr ) >> numberOfOutputs; + numberOfOutputs++; + stringstream ss; + ss << numberOfOutputs; + xmlSetProp(neuralOutputsNode, (const xmlChar*)"numberOfOutputs", (const xmlChar*)(ss.str().c_str()) ); + + // then append the node and its children + xmlNodePtr neuralOutputNode = xmlNewChild(neuralOutputsNode, 0, (const xmlChar*)"NeuralOutput", 0); + ss.str(""); ss << outputNeuron; + xmlNewProp(neuralOutputNode, (const xmlChar*)"outputNeuron", (const xmlChar*)(ss.str().c_str()) ); + + xmlNodePtr derivedFieldNode = xmlNewChild(neuralOutputNode, 0, (const xmlChar*)"DerivedField", 0); + xmlNewProp(derivedFieldNode, (const xmlChar*)"optype", (const xmlChar*)(optype.c_str()) ); + xmlNewProp(derivedFieldNode, (const xmlChar*)"dataType", (const xmlChar*)(dataType.c_str()) ); + + xmlNodePtr normcontNode = xmlNewChild(derivedFieldNode, 0, (const xmlChar*)"NormContinuous", 0); + xmlNewProp(normcontNode, (const xmlChar*)"field", (const xmlChar*)(outputName.c_str()) ); + + xmlNodePtr node_linearnorm1 = xmlNewChild(normcontNode, 0, (const xmlChar*)"LinearNorm", 0); + ss.str(""); ss << scientific << orig1; + xmlNewProp(node_linearnorm1, (const xmlChar*)"orig", (const xmlChar*)(ss.str().c_str()) ); + ss.str(""); ss << scientific << norm1; + xmlNewProp(node_linearnorm1, (const xmlChar*)"norm", (const xmlChar*)(ss.str().c_str()) ); + xmlNodePtr node_linearnorm2 = xmlNewChild(normcontNode, 0, (const xmlChar*)"LinearNorm", 0); + ss.str(""); ss << scientific << orig2; + xmlNewProp(node_linearnorm2, (const xmlChar*)"orig", (const xmlChar*)(ss.str().c_str()) ); + ss.str(""); ss << scientific << norm2; + xmlNewProp(node_linearnorm2, (const xmlChar*)"norm", (const xmlChar*)(ss.str().c_str()) ); +} + + /** + * Add a NeuralLayer node to the current model. + * @brief Specific to NeuralNetwork + * @param activationFunction Activation function. One of kIDENTITY, kTANH, kLOGISTIC. + */ +void PMMLlib::AddNeuralLayer(PMMLActivationFunction activationFunction) +{ + CheckNeuralNetwork(); + + string functionName; + switch(activationFunction) + { + case kIDENTITY: + functionName = "identity"; + break; + case kTANH: + functionName = "tanh"; + break; + case kLOGISTIC: + functionName = "logistic"; + break; + } + xmlNodePtr netNode = _currentModelNode; + // Increment the number of layers + string numberOfLayersStr = _getProp(_currentModelNode, string("numberOfLayers")); + int numberOfLayers; + istringstream( numberOfLayersStr ) >> numberOfLayers; + numberOfLayers++; + stringstream ss; + ss << numberOfLayers; + xmlSetProp(netNode, (const xmlChar*)"numberOfLayers", (const xmlChar*)(ss.str().c_str()) ); + // Add the neural layer node + xmlNodePtr neuralLayerNode = xmlNewChild(netNode, 0, (const xmlChar*)"NeuralLayer", 0); + xmlNewProp(neuralLayerNode, (const xmlChar*)"activationFunction", (const xmlChar*)(functionName.c_str()) ); + xmlNewProp(neuralLayerNode, (const xmlChar*)"numberOfNeurons", (const xmlChar*)"0" ); + // Save the current layer in the _currentNode attribute + _currentNode = neuralLayerNode; +} + + /** + * Add a NeuralLayer node to the current model. + * @brief Specific to NeuralNetwork + * @param id Id of the layer + * @param bias Value of property "bias" + * @param conNb Number of Con nodes + * @param firstFrom Value of property "from" for the first Con + * @param weights Vector of weights (One per Con node) + */ +void PMMLlib::AddNeuron(int id, + double bias, + int conNb, + int firstFrom, + vector weights) +{ + CheckNeuralNetwork(); + + stringstream ss; + + // increment the number of neurons + string numberOfNeuronsStr = _getProp(_currentNode, string("numberOfNeurons")); + int numberOfNeurons; + istringstream( numberOfNeuronsStr ) >> numberOfNeurons; + numberOfNeurons++; + ss << numberOfNeurons; + xmlSetProp(_currentNode, (const xmlChar*)"numberOfNeurons", (const xmlChar*)(ss.str().c_str()) ); + + // append a neuron + xmlNodePtr neuronNode = xmlNewChild(_currentNode, 0, (const xmlChar*)"Neuron", 0); + ss.str(""); ss << id; + xmlNewProp(neuronNode, (const xmlChar*)"id", (const xmlChar*)(ss.str().c_str()) ); + ss.str(""); ss << scientific << bias; + xmlNewProp(neuronNode, (const xmlChar*)"bias", (const xmlChar*)(ss.str().c_str()) ); + + // append multiple 'Con' to the neuron + for(int k=0 ; k &minInput, + vector &maxInput, + vector &minOutput, + vector &maxOutput, + vector &valW ) +{ + CheckNeuralNetwork(); + + xmlNodePtr netNode = _currentModelNode ; + // Get the different values required + // Build min/max input/output vectors + for(int i=0 ; ichildren; + for(int j = 0;jnext; + } + node_inputs = node_inputs->children; // DerivedField + node_inputs = node_inputs->children; // NormContinuous + node_inputs = node_inputs->children; // LinearNorm + string strOrig1 = _getProp(node_inputs, string("orig") ); + double orig1 = atof( strOrig1.c_str() ); + string strNorm1 = _getProp(node_inputs, string("norm") ); + double norm1 = atof( strNorm1.c_str() ); + node_inputs = node_inputs->next; + string strOrig2 = _getProp(node_inputs, string("orig") ); + double orig2 = atof( strOrig2.c_str() ); + string strNorm2 = _getProp(node_inputs, string("norm") ); + if( normType==0 ) + { // kMinusOneOne + minInput[i] = orig1; + maxInput[i] = orig2; + } + else + { // kCR, kZeroOne + minInput[i] = orig2; + maxInput[i] = -1.0*norm1*orig2; + } + } + xmlNodePtr node_outputs = GetChildByName(netNode,"NeuralOutputs"); + node_outputs = node_outputs->children; + node_outputs = node_outputs->children; // DerivedField + node_outputs = node_outputs->children; // NormContinuous + node_outputs = node_outputs->children; // LinearNorm + string strOrig1 = _getProp(node_outputs, string("orig") ); + double orig1 = atof( strOrig1.c_str() ); + string strNorm1 = _getProp(node_outputs, string("norm") ); + double norm1 = atof( strNorm1.c_str() ); + node_outputs = node_outputs->next; + string strOrig2 = _getProp(node_outputs, string("orig") ); + double orig2 = atof( strOrig2.c_str() ); + if( normType==0 ) + { // kMinusOneOne + minOutput[0] = orig1; + maxOutput[0] = orig2; + } + else + { // kCR, kZeroOne + minOutput[0] = orig2; + maxOutput[0] = -1.0*norm1*orig2; + } + // Build weight vector + for(int j=0 ; j