MeLOn
ffNet.h
Go to the documentation of this file.
1 /**********************************************************************************
2 * Copyright (c) 2020 Process Systems Engineering (AVT.SVT), RWTH Aachen University
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License 2.0 which is available at
6 * http://www.eclipse.org/legal/epl-2.0.
7 *
8 * SPDX-License-Identifier: EPL-2.0
9 *
10 * @file ffNet.h
11 *
12 * @brief File containing declaration of the FeedForwardNet class.
13 *
14 **********************************************************************************/
15 
16 #pragma once
17 
18 #include <vector> // std::vector a
19 #include <cmath> // std::tanh
20 #include <algorithm> // std::min, std::max, std::transform
21 #include <string> // std::string, std::to_string
22 #include <utility> // std::pair, std::make_pair
23 #include <memory> // std::shared_ptr, std::make_shared
24 
25 #include "vectorarithmetics.h"
26 #include "exceptions.h"
27 #include "MeLOn.h"
28 #include "AnnProperties.h"
29 #include "AnnParser.h"
30 
31 namespace melon {
32 
37  enum TANH_REFORMULATION {
38  TANH_REF_0 = 0,
39  TANH_REF1,
40  TANH_REF2,
41  TANH_REF3,
42  TANH_REF4
43  };
44 
51  template <typename T>
52  class FeedForwardNet : public MelonModel<T> {
53  private:
54 
55  std::shared_ptr<const AnnData> _annData;
56 
57  std::unique_ptr<Scaler<T>> _inputScaler;
58  std::unique_ptr<Scaler<T>> _outputScaler;
61  T(*_tanh_formulation) (T) = &_tanh;
62  inline static T _tanh(T x) { using std::tanh; return tanh(x); }
63  inline static T _tanh_reformulation_1(T x) { return (exp(x) - exp(-1 * x)) / (exp(x) + exp(-1 * x)); }
64  inline static T _tanh_reformulation_2(T x) { return (exp(2 * x) - 1) / (exp(2 * x) + 1); }
65  inline static T _tanh_reformulation_3(T x) { return 1 - 2 / (1 + exp(2 * x)); }
66  inline static T _tanh_reformulation_4(T x) { return (1 - exp(-2 * x)) / (1 + exp(-2 * x)); }
67 
68  inline static T _relu(T x) { using std::max; return max((T)0, x); }
69  inline static T _relu6(T x) { using std::max; using std::min; return min((T)6, max((T)0, x)); }
70 
71 
81  std::vector<T> _calculate_layer_activation(const std::vector<T>& v, const ACTIVATION_FUNCTION activationFunction);
82 
96  std::vector<T> _calculate_prediction(const std::vector<T> input, const std::vector<T> internalVariables, const bool fullSpace, std::vector<T>& constraints);
97 
103  void _set_data_object(std::shared_ptr<const ModelData> modelData) override;
104 
105  public:
106 
110  FeedForwardNet() : MelonModel<T>(std::make_shared<AnnParserFactory>()) {};
111 
119  FeedForwardNet(std::string modelName, MODEL_FILE_TYPE fileType) : FeedForwardNet() { this->load_model(modelName, fileType); };
120 
130  FeedForwardNet(std::string modelPath, std::string modelName, MODEL_FILE_TYPE fileType) : FeedForwardNet() { this->load_model(modelPath, modelName, fileType); };
131 
137  FeedForwardNet(std::shared_ptr<AnnData> modelData) : FeedForwardNet() { this->load_model(modelData); };
138 
142  ~FeedForwardNet() = default;
143 
151  std::vector<T> calculate_prediction_reduced_space(const std::vector<T> input);
152 
164  std::vector<T> calculate_prediction_full_space(const std::vector<T> input, const std::vector<T> internalVariables, std::vector<T>& constraints);
165 
171  void set_tanh_formulation(const TANH_REFORMULATION& reformulation);
172 
178  unsigned int get_number_of_full_space_variables();
179 
189  void get_full_space_variables(unsigned int& variableNumber, std::vector<std::string>& variableNames, std::vector<std::pair<double, double>>& variableBounds);
190 
191  };
192 
194  // Set data object containing model parameters
195  template<typename T>
196  void FeedForwardNet<T>::_set_data_object(std::shared_ptr<const ModelData> modelData) {
197 
198  // Check if you can downcast the ModelData pointer to an AnnData pointer
199  _annData = std::dynamic_pointer_cast<const AnnData>(modelData);
200  if (_annData == nullptr) {
201  throw(MelonException(" Error while loading feed forward network: Incorrect type of passed data object. The data object must be of type AnnData."));
202  }
203 
204  _inputScaler = ScalerFactory<T>::create_scaler(_annData->inputScalerData);
205  _outputScaler = ScalerFactory<T>::create_scaler(_annData->outputScalerData);
206  }
207 
209  // Calculates the prediction of the feed forward net for a given point in reduced space mode (only values network inputs are given)
210  template <typename T>
211  std::vector<T> FeedForwardNet<T>::calculate_prediction_reduced_space(std::vector<T> input) {
212  std::vector<T> dummyConstraints;
213  std::vector<T> dummyInternalVariables;
214  try {
215  return _calculate_prediction(input, dummyInternalVariables, false, dummyConstraints);
216  }
217  catch (const std::exception& e) {
218  throw(MelonException(" Encountered a fatal error while evaluating feed forward network. Terminating.", e));
219  }
220  catch (...) {
221  throw(MelonException(" Encountered a fatal error while evaluating feed forward network. Terminating."));
222  }
223  }
224 
225 
227  // Calculates the prediction of the feed forward net for a given point in full space mode (values for all internal variables are given and a set of constraints is returned)
228  template <typename T>
229  std::vector<T> FeedForwardNet<T>::calculate_prediction_full_space(const std::vector<T> input, const std::vector<T> internalVariables, std::vector<T>& constraints) {
230  try {
231  return _calculate_prediction(input, internalVariables, true, constraints);
232  }
233  catch (const std::exception& e) {
234  throw(MelonException(" Encountered a fatal error while evaluating feed forward network. Terminating.", e));
235  }
236  catch (...) {
237  throw(MelonException(" Encountered a fatal error while evaluating feed forward network. Terminating."));
238  }
239  }
240 
241 
243  // Changes the reformulation to be used for tanh evaluations.
244  template <typename T>
246  switch (reformulation) {
248  this->_tanh_formulation = &_tanh;
249  break;
251  this->_tanh_formulation = &_tanh_reformulation_1;
252  break;
254  this->_tanh_formulation = &_tanh_reformulation_2;
255  break;
257  this->_tanh_formulation = &_tanh_reformulation_3;
258  break;
260  this->_tanh_formulation = &_tanh_reformulation_4;
261  break;
262  default:
263  throw MelonException(" Error while setting tanh formulation: Unknown tanh formulation.");
264  }
265  }
266 
267 
269  // Get the number of internal network variables
270  template <typename T>
273  throw MelonException(" Error: No network loaded.");
274  }
275 
276  unsigned int variableNumber;
277  std::vector<std::string> dummyVariableNames;
278  std::vector<std::pair<double, double>> dummyVariableBounds;
279 
280  get_full_space_variables(variableNumber, dummyVariableNames, dummyVariableBounds);
281 
282  return variableNumber;
283  }
284 
285 
287  // Returns the number and the names of the internal variables of the network
288  template <typename T>
289  void FeedForwardNet<T>::get_full_space_variables(unsigned int& variableNumber, std::vector<std::string>& variableNames, std::vector<std::pair<double, double>>& variableBounds) {
291  throw MelonException(" Error: No network loaded.");
292  }
293 
294  // Create aliases for data
295  auto& structure = _annData->structure;
296  auto& weights = _annData->weights;
297 
298  const double MAX_BOUND = 10e6;
299  variableNumber = 0;
300  variableNames.clear();
301  variableBounds.clear();
302 
303  // Normalized input
304  if (structure.scaledInput) {
305  variableNumber += structure.inputSize;
306  for (int i = 0; i < structure.inputSize; i++) {
307  variableNames.push_back("input_normalized_" + std::to_string(i));
308  variableBounds.push_back(std::make_pair(-1., 1.));
309  }
310  }
311 
312  for (int iLayer = 0; iLayer < structure.numLayers; iLayer++) {
313 
314  // Accumulated layer inputs
315  variableNumber += structure.layerSize.at(iLayer);
316  for (int iNeuron = 0; iNeuron < structure.layerSize.at(iLayer); iNeuron++) {
317  variableNames.push_back("layer_" + std::to_string(iLayer) + "_neuron_" + std::to_string(iNeuron) + "_acummulated_input");
318  variableBounds.push_back(std::make_pair(-MAX_BOUND, MAX_BOUND));
319  }
320 
321  // Layer outputs
322  variableNumber += structure.layerSize.at(iLayer);
323  for (int iNeuron = 0; iNeuron < structure.layerSize.at(iLayer); iNeuron++) {
324  variableNames.push_back("layer_" + std::to_string(iLayer) + "_neuron_" + std::to_string(iNeuron) + "_output");
325  variableBounds.push_back(std::make_pair(-MAX_BOUND, MAX_BOUND));
326  }
327  }
328 
329  // Denormalized ouput
330  if (structure.normalizedOutput) {
331  variableNumber += structure.layerSize.back();
332  for (int i = 0; i < structure.layerSize.back(); i++) {
333  variableNames.push_back("output_" + std::to_string(i));
334  variableBounds.push_back(std::make_pair(-MAX_BOUND, MAX_BOUND));
335  }
336  }
337  }
338 
339 
341  // Calculates the prediction of the feed forward net for a given point
342  template <typename T>
343  std::vector<T> FeedForwardNet<T>::_calculate_prediction(const std::vector<T> input, const std::vector<T> internalVariables, const bool fullSpace, std::vector<T>& constraints) {
345  // ---------------------------------------------------------------------------------
346  // 0: Initialization
347  // ---------------------------------------------------------------------------------
348 
349  // Create aliases for data
350  auto& structure = _annData->structure;
351  auto& weights = _annData->weights;
352 
353  // Check if variables vector has correct size
354  if (input.size() != structure.inputSize) {
355  throw MelonException(" Error while evaluating network: Incorrect number of variables. In reduced space mode evaluation the number of variables must be equal to the number of network inputs.");
356  }
357  if (fullSpace) {
358  unsigned int variablesSize = get_number_of_full_space_variables();
359  if (internalVariables.size() != variablesSize) {
360  throw MelonException(" Error while evaluating network: Incorrect number of variables. In full space mode evaluation the number of variables must be equal to the number of internal network variables.");
361  }
362  }
363 
364  std::vector<std::vector<T>> networkValues(structure.numLayers);
365 
366  auto variableIterator = internalVariables.begin();
367 
368  // ---------------------------------------------------------------------------------
369  // 1: Normalize the input
370  // ---------------------------------------------------------------------------------
371  std::vector<T> normalizedInput = input;
372  if (structure.scaledInput) {
373  normalizedInput = _inputScaler->scale(input);
374  if (fullSpace) {
375  this->_set_constraints(constraints, normalizedInput, variableIterator);
376  }
377  }
378 
379 
380  // ---------------------------------------------------------------------------------
381  // 2: Evaluate each layer in the network
382  // ---------------------------------------------------------------------------------
383  for (size_t iLayer = 0; iLayer < networkValues.size(); iLayer++) {
384 
385  // Create aliases for better code readability
386  auto& layerInputWeights = weights.inputWeight.at(iLayer);
387  auto& layerBiasWeights = weights.biasWeight.at(iLayer);
388  auto& layerIncidentLayerWeights = weights.layerWeight.at(iLayer);
389  auto& layerConnections = structure.layerConnect.at(iLayer);
390  auto& layerSize = structure.layerSize.at(iLayer);
391 
392  // 1a Calculate weighted sum
393  std::vector<T> accumulatedLayerInputs(structure.layerSize.at(iLayer), 0);
394 
395  // - input variables incident to the current layer
396  if (structure.inputConnect.at(iLayer) == 1) {
397  accumulatedLayerInputs = accumulatedLayerInputs + layerInputWeights * normalizedInput;
398  }
399 
400  // - bias for the current layer
401  if (structure.biasConnect.at(iLayer) == 1) {
402  accumulatedLayerInputs = accumulatedLayerInputs + layerBiasWeights;
403  }
404 
405  // - values from previous layers that are incident to the current layer
406  for (size_t iIncidentLayer = 0; iIncidentLayer < layerConnections.size(); iIncidentLayer++) {
407  if (layerConnections.at(iIncidentLayer) == 1) {
408  accumulatedLayerInputs = accumulatedLayerInputs + layerIncidentLayerWeights.at(iIncidentLayer)*networkValues.at(iIncidentLayer);
409  }
410  }
411 
412  if (fullSpace) {
413  this->_set_constraints(constraints, accumulatedLayerInputs, variableIterator);
414  }
415 
416  // 2b Calculate the layer output by evaluating the activation function on the weighted sum
417  std::vector<T> layerOutput = _calculate_layer_activation(accumulatedLayerInputs, structure.activationFunction.at(iLayer));
418 
419  if (fullSpace) {
420  this->_set_constraints(constraints, layerOutput, variableIterator);
421  }
422 
423  networkValues.at(iLayer) = layerOutput;
424  }
425 
426  // ---------------------------------------------------------------------------------
427  // 2: Denormalize network output
428  // ---------------------------------------------------------------------------------
429  std::vector<T> output = networkValues.back();
430  if (structure.normalizedOutput) {
431  output = _outputScaler->descale(networkValues.back());
432  if (fullSpace) {
433  this->_set_constraints(constraints, output, variableIterator);
434  }
435  }
436 
437  return output;
438  }
439  else {
440  throw MelonException(" Error while evaluating network: No network loaded.");
441  }
442  };
443 
444 
446  // Calculates the activations for a layer given its inputs.
447  template <typename T>
448  std::vector<T> FeedForwardNet<T>::_calculate_layer_activation(const std::vector<T> &v, const ACTIVATION_FUNCTION activationFunction) {
449 
450  std::vector<T> layerActivation(v);
451 
452  // Select correct activation function
453  T(*activation_function) (T);
454  switch (activationFunction) {
456  return layerActivation;
458  activation_function = this->_tanh_formulation;
459  break;
461  activation_function = &_relu;
462  break;
464  activation_function = &_relu6;
465  break;
466  }
467 
468  // Apply activation function to all entries in v (layer neurons) and write the results to layerActivation
469  std::transform(v.begin(), v.end(), layerActivation.begin(), activation_function);
470 
471  return layerActivation;
472  };
473 }
melon::MODEL_FILE_TYPE
MODEL_FILE_TYPE
Enum for representing the parsable filetypes.
Definition: modelParser.h:43
melon::FeedForwardNet
This class represents a feed foward artificial network to be used in the MAiNGO solver.
Definition: ffNet.h:65
melon::FeedForwardNet::_tanh
static T _tanh(T x)
Definition: ffNet.h:75
exceptions.h
melon::MelonModel
Definition: MeLOn.h:45
melon::TANH_REF_0
@ TANH_REF_0
Definition: ffNet.h:51
vectorarithmetics.h
melon::FeedForwardNet::_relu6
static T _relu6(T x)
Definition: ffNet.h:82
melon::FeedForwardNet::_inputScaler
std::unique_ptr< Scaler< T > > _inputScaler
Definition: ffNet.h:70
melon::TANH_REF1
@ TANH_REF1
Definition: ffNet.h:52
melon::FeedForwardNet::_tanh_reformulation_1
static T _tanh_reformulation_1(T x)
Definition: ffNet.h:76
MeLOn.h
melon::ACTIVATION_FUNCTION
ACTIVATION_FUNCTION
Enum for representing the available types of scaler parameters.
Definition: AnnProperties.h:43
melon::FeedForwardNet::calculate_prediction_reduced_space
std::vector< T > calculate_prediction_reduced_space(const std::vector< T > input)
Calculates the prediction of the feed forward net for a given point in reduced space mode (only value...
Definition: ffNet.h:224
melon::TANH_REF3
@ TANH_REF3
Definition: ffNet.h:54
melon::TANH_REF2
@ TANH_REF2
Definition: ffNet.h:53
melon::FeedForwardNet::_tanh_formulation
T(* _tanh_formulation)(T)
Definition: ffNet.h:74
melon::TANH
@ TANH
Definition: AnnProperties.h:45
melon::FeedForwardNet::set_tanh_formulation
void set_tanh_formulation(const TANH_REFORMULATION &reformulation)
Changes the reformulation to be used for tanh evaluations. The reformulations are intended to be used...
Definition: ffNet.h:258
melon::RELU
@ RELU
Definition: AnnProperties.h:46
melon::FeedForwardNet::_tanh_reformulation_3
static T _tanh_reformulation_3(T x)
Definition: ffNet.h:78
melon::MelonModel::_modelLoaded
bool _modelLoaded
Definition: MeLOn.h:94
melon::AnnParserFactory
This class is a factory class for creating child instances of AnnParser.
Definition: AnnParser.h:228
melon::PURE_LIN
@ PURE_LIN
Definition: AnnProperties.h:44
melon::FeedForwardNet::calculate_prediction_full_space
std::vector< T > calculate_prediction_full_space(const std::vector< T > input, const std::vector< T > internalVariables, std::vector< T > &constraints)
Calculates the prediction of the feed forward net for a given point in full space mode (values for al...
Definition: ffNet.h:242
melon::FeedForwardNet::_tanh_reformulation_4
static T _tanh_reformulation_4(T x)
Definition: ffNet.h:79
melon::MelonModel::load_model
void load_model(std::string modelName, MODEL_FILE_TYPE fileType)
Loads new model from file.
Definition: MeLOn.h:127
melon::TANH_REF4
@ TANH_REF4
Definition: ffNet.h:55
melon::TANH_REFORMULATION
TANH_REFORMULATION
Enum for representing the different reformulations for the tanh activation function.
Definition: ffNet.h:50
melon::FeedForwardNet::_tanh_reformulation_2
static T _tanh_reformulation_2(T x)
Definition: ffNet.h:77
melon::FeedForwardNet::get_number_of_full_space_variables
unsigned int get_number_of_full_space_variables()
Get the number of internal network variables.
Definition: ffNet.h:284
AnnParser.h
melon::FeedForwardNet::_calculate_prediction
std::vector< T > _calculate_prediction(const std::vector< T > input, const std::vector< T > internalVariables, const bool fullSpace, std::vector< T > &constraints)
Calculates the prediction of the feed forward net for a given point.
Definition: ffNet.h:356
melon::FeedForwardNet::~FeedForwardNet
~FeedForwardNet()=default
Default Destructor.
MelonException
This class defines the exceptions thrown by FeedForwardNet.
Definition: exceptions.h:32
melon::FeedForwardNet::_annData
std::shared_ptr< const AnnData > _annData
Definition: ffNet.h:68
AnnProperties.h
melon::FeedForwardNet::_relu
static T _relu(T x)
Definition: ffNet.h:81
melon::RELU6
@ RELU6
Definition: AnnProperties.h:47
melon
Definition: kernel.h:21
melon::ScalerFactory::create_scaler
static std::unique_ptr< Scaler< T > > create_scaler(const std::shared_ptr< const ScalerData > scalerData)
Factory function for creating a instance of a scaler object.
Definition: scaler.h:221
melon::FeedForwardNet::_outputScaler
std::unique_ptr< Scaler< T > > _outputScaler
Definition: ffNet.h:71
melon::FeedForwardNet::_set_data_object
void _set_data_object(std::shared_ptr< const ModelData > modelData) override
Sets data object containing model parameters.
Definition: ffNet.h:209
melon::FeedForwardNet::get_full_space_variables
void get_full_space_variables(unsigned int &variableNumber, std::vector< std::string > &variableNames, std::vector< std::pair< double, double >> &variableBounds)
Returns the number and the names of the internal variables of the network.
Definition: ffNet.h:302
melon::FeedForwardNet::FeedForwardNet
FeedForwardNet()
Constructor for creating object with no model loaded.
Definition: ffNet.h:123
melon::FeedForwardNet::_calculate_layer_activation
std::vector< T > _calculate_layer_activation(const std::vector< T > &v, const ACTIVATION_FUNCTION activationFunction)
Calculates the activations for a layer given its inputs.
Definition: ffNet.h:461