n2p2 - A neural network potential package
nnp::NeuralNetwork Class Reference

This class implements a feed-forward neural network. More...

#include <NeuralNetwork.h>

Collaboration diagram for nnp::NeuralNetwork:

Classes

struct  Layer
 One neural network layer. More...
 
struct  Neuron
 A single neuron. More...
 

Public Types

enum  ActivationFunction {
  AF_UNSET , AF_IDENTITY , AF_TANH , AF_LOGISTIC ,
  AF_SOFTPLUS , AF_RELU , AF_GAUSSIAN , AF_COS ,
  AF_REVLOGISTIC , AF_EXP , AF_HARMONIC
}
 List of available activation function types. More...
 
enum  ModificationScheme {
  MS_ZEROBIAS , MS_ZEROOUTPUTWEIGHTS , MS_FANIN , MS_GLOROTBENGIO ,
  MS_NGUYENWIDROW , MS_PRECONDITIONOUTPUT
}
 List of available connection modification schemes. More...
 

Public Member Functions

 NeuralNetwork (int numLayers, int const *const &numNeuronsPerLayer, ActivationFunction const *const &activationFunctionsPerLayer)
 Neural network class constructor. More...
 
 ~NeuralNetwork ()
 
void setNormalizeNeurons (bool normalizeNeurons)
 Turn on/off neuron normalization. More...
 
int getNumNeurons () const
 Return total number of neurons. More...
 
int getNumConnections () const
 Return total number of connections. More...
 
int getNumWeights () const
 Return number of weights. More...
 
int getNumBiases () const
 Return number of biases. More...
 
void setConnections (double const *const &connections)
 Set neural network weights and biases. More...
 
void getConnections (double *connections) const
 Get neural network weights and biases. More...
 
void initializeConnectionsRandomUniform (unsigned int seed)
 Initialize connections with random numbers. More...
 
void modifyConnections (ModificationScheme modificationScheme)
 Change connections according to a given modification scheme. More...
 
void modifyConnections (ModificationScheme modificationScheme, double parameter1, double parameter2)
 Change connections according to a given modification scheme. More...
 
void setInput (double const *const &input) const
 Set neural network input layer node values. More...
 
void setInput (std::size_t const index, double const value) const
 Set neural network input layer node values. More...
 
void getOutput (double *output) const
 Get neural network output layer node values. More...
 
void propagate ()
 Propagate input information through all layers. More...
 
void calculateDEdG (double *dEdG) const
 Calculate derivative of output neuron with respect to input neurons. More...
 
void calculateDEdc (double *dEdc) const
 Calculate derivative of output neuron with respect to connections. More...
 
void calculateDFdc (double *dFdc, double const *const &dGdxyz) const
 Calculate "second" derivative of output with respect to connections. More...
 
void writeConnections (std::ofstream &file) const
 Write connections to file. More...
 
void getNeuronStatistics (long *count, double *min, double *max, double *sum, double *sum2) const
 Return gathered neuron statistics. More...
 
void resetNeuronStatistics ()
 Reset neuron statistics. More...
 
long getMemoryUsage ()
 
std::vector< std::string > info () const
 Print neural network architecture. More...
 

Private Member Functions

void calculateDEdb (double *dEdb) const
 Calculate derivative of output neuron with respect to biases. More...
 
void calculateDxdG (int index) const
 Calculate derivative of neuron values before activation function with respect to input neuron. More...
 
void calculateD2EdGdc (int index, double const *const &dEdb, double *d2EdGdc) const
 Calculate second derivative of output neuron with respect to input neuron and connections. More...
 
void allocateLayer (Layer &layer, int numNeuronsPrevLayer, int numNeurons, ActivationFunction activationFunction)
 Allocate a single layer. More...
 
void propagateLayer (Layer &layer, Layer &layerPrev)
 Propagate information from one layer to the next. More...
 

Private Attributes

bool normalizeNeurons
 If neurons are normalized. More...
 
int numWeights
 Number of NN weights only. More...
 
int numBiases
 Number of NN biases only. More...
 
int numConnections
 Number of NN connections (weights + biases). More...
 
int numLayers
 Total number of layers (includes input and output layers). More...
 
int numHiddenLayers
 Number of hidden layers. More...
 
int * weightOffset
 Offset adress of weights per layer in combined weights+bias array. More...
 
int * biasOffset
 Offset adress of biases per layer in combined weights+bias array. More...
 
int * biasOnlyOffset
 Offset adress of biases per layer in bias only array. More...
 
LayerinputLayer
 Pointer to input layer. More...
 
LayeroutputLayer
 Pointer to output layer. More...
 
Layerlayers
 Neural network layers. More...
 

Detailed Description

This class implements a feed-forward neural network.

Definition at line 28 of file NeuralNetwork.h.

Member Enumeration Documentation

◆ ActivationFunction

List of available activation function types.

Enumerator
AF_UNSET 

Unset activation function.

AF_IDENTITY 

\(f_a(x) = x\)

AF_TANH 

\(f_a(x) = \tanh(x)\)

AF_LOGISTIC 

\(f_a(x) = 1 / (1 + \mathrm{e}^{-x})\)

AF_SOFTPLUS 

\(f_a(x) = \ln (1 + \mathrm{e}^x)\)

AF_RELU 

\(f_a(x) = \max(0, x)\) (NOT recommended for HDNNPs!)

AF_GAUSSIAN 

\(f_a(x) = \mathrm{e}^{-x^2 / 2}\)

AF_COS 

\(f_a(x) = \cos (x)\)

AF_REVLOGISTIC 

\(f_a(x) = 1 - 1 / (1 + \mathrm{e}^{-x})\)

AF_EXP 

\(f_a(x) = \mathrm{e}^{-x}\)

AF_HARMONIC 

\(f_a(x) = x^2\)

Definition at line 32 of file NeuralNetwork.h.

◆ ModificationScheme

List of available connection modification schemes.

Enumerator
MS_ZEROBIAS 

Set all bias values to zero.

MS_ZEROOUTPUTWEIGHTS 

Set all weights connecting to the output layer to zero.

MS_FANIN 

Normalize weights via number of neuron inputs (fan-in).

   If initial weights are uniformly distributed in
   @f$\left[-1, 1\right]@f$ they will be scaled to be in
   @f$\left[\frac{-1}{\sqrt{n_\text{in}}},
   \frac{1}{\sqrt{n_\text{in}}}\right]@f$, where @f$n_\text{in}@f$ is
   the number of incoming weights of a neuron (if activation
   function is of type #AF_TANH).
MS_GLOROTBENGIO 

Normalize connections according to Glorot and Bengio.

   If initial weights are uniformly distributed in
   @f$\left[-1, 1\right]@f$ they will be scaled to be in
   @f$\left[-\sqrt{\frac{6}{n_\text{in} + n_\text{out}}},
   \sqrt{\frac{6}{n_\text{in} + n_\text{out}}}\right]@f$, where
   @f$n_\text{in}@f$ and @f$n_\text{out}@f$ are the number of incoming
   and outgoing weights of a neuron, respectively (if activation
   function is of type #AF_TANH).

   For details see:
    - X. Glorot and Y. Bengio, "Understanding the difficulty of
      training deep feedforward neural networks", International
      conference on artificial intelligence and statistics. 2010.
MS_NGUYENWIDROW 

Initialize connections according to Nguyen-Widrow scheme.

   For details see:
    - D. Nguyen and B. Widrow, Improving the learning speed of 2-layer
      neural networks by choosing initial values of the adaptive
      weights, Proceedings of the International Joint Conference on
      Neural networks (IJCNN), pages 21-26, San Diego, 1990
    - T. Morawietz, Entwicklung eines effizienten Potentials für das
      Wasser-Dimer basierend auf künstlichen neuronalen Netzen,
      Master's thesis, pages 24-28, Bochum, 2010
MS_PRECONDITIONOUTPUT 

Apply preconditioning to output layer connections.

   Multiply weights connecting to output neurons with @f$\sigma@f$ and
   add "mean" to biases.

   Call #modifyConnections with two additional arguments:
   #modifyConnections(NeuralNetwork::MS_PRECONDITIONOUTPUT,
   mean, sigma);

Definition at line 59 of file NeuralNetwork.h.

60 {
113 };
@ MS_ZEROOUTPUTWEIGHTS
Set all weights connecting to the output layer to zero.
Definition: NeuralNetwork.h:64
@ MS_ZEROBIAS
Set all bias values to zero.
Definition: NeuralNetwork.h:62
@ MS_PRECONDITIONOUTPUT
Apply preconditioning to output layer connections.
@ MS_FANIN
Normalize weights via number of neuron inputs (fan-in).
Definition: NeuralNetwork.h:74
@ MS_GLOROTBENGIO
Normalize connections according to Glorot and Bengio.
Definition: NeuralNetwork.h:90
@ MS_NGUYENWIDROW
Initialize connections according to Nguyen-Widrow scheme.

Constructor & Destructor Documentation

◆ NeuralNetwork()

NeuralNetwork::NeuralNetwork ( int  numLayers,
int const *const &  numNeuronsPerLayer,
ActivationFunction const *const &  activationFunctionsPerLayer 
)

Neural network class constructor.

Parameters
[in]numLayersTotal number of layers (including in- and output layer).
[in]numNeuronsPerLayerArray with number of neurons per layer.
[in]activationFunctionsPerLayerArray with activation function type per layer (note: input layer activation function is is mandatory although it is never used).

Definition at line 30 of file NeuralNetwork.cpp.

34{
35 // check number of layers
36 this->numLayers = numLayers;
37 if (numLayers < 3)
38 {
39 fprintf(stderr,
40 "ERROR: Neural network must have at least three layers\n");
41 exit(EXIT_FAILURE);
42 }
44
45 // do not normalize neurons by default
46 normalizeNeurons = false;
47
48 // allocate layers and populate with neurons
49 layers = new Layer[numLayers];
50 inputLayer = &layers[0];
53 0,
54 numNeuronsPerLayer[0],
55 activationFunctionsPerLayer[0]);
56 for (int i = 1; i < numLayers; i++)
57 {
59 numNeuronsPerLayer[i-1],
60 numNeuronsPerLayer[i],
61 activationFunctionsPerLayer[i]);
62 }
63
64 // count connections
65 numWeights = 0;
66 numBiases = 0;
68 for (int i = 1; i < numLayers; i++)
69 {
72 }
74
75 // calculate weight and bias offsets for each layer
76 weightOffset = new int[numLayers-1];
77 weightOffset[0] = 0;
78 for (int i = 1; i < numLayers-1; i++)
79 {
80 weightOffset[i] = weightOffset[i-1] +
81 (layers[i-1].numNeurons + 1) * layers[i].numNeurons;
82 }
83 biasOffset = new int[numLayers-1];
84 for (int i = 0; i < numLayers-1; i++)
85 {
86 biasOffset[i] = weightOffset[i] +
88 }
89 biasOnlyOffset = new int[numLayers-1];
90 biasOnlyOffset[0] = 0;
91 for (int i = 1; i < numLayers-1; i++)
92 {
94 }
95}
Layer * inputLayer
Pointer to input layer.
int * biasOnlyOffset
Offset adress of biases per layer in bias only array.
int numLayers
Total number of layers (includes input and output layers).
bool normalizeNeurons
If neurons are normalized.
void allocateLayer(Layer &layer, int numNeuronsPrevLayer, int numNeurons, ActivationFunction activationFunction)
Allocate a single layer.
int * weightOffset
Offset adress of weights per layer in combined weights+bias array.
int * biasOffset
Offset adress of biases per layer in combined weights+bias array.
int numBiases
Number of NN biases only.
int numConnections
Number of NN connections (weights + biases).
Layer * layers
Neural network layers.
int numWeights
Number of NN weights only.
Layer * outputLayer
Pointer to output layer.
int numHiddenLayers
Number of hidden layers.
int numNeurons
Number of neurons in this layer .
int numNeuronsPrevLayer
Number of neurons in previous layer .

References allocateLayer(), biasOffset, biasOnlyOffset, inputLayer, layers, normalizeNeurons, numBiases, numConnections, numHiddenLayers, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, numWeights, outputLayer, and weightOffset.

Here is the call graph for this function:

◆ ~NeuralNetwork()

NeuralNetwork::~NeuralNetwork ( )

Definition at line 97 of file NeuralNetwork.cpp.

98{
99 for (int i = 0; i < numLayers; i++)
100 {
101 for (int j = 0; j < layers[i].numNeurons; j++)
102 {
103 delete[] layers[i].neurons[j].weights;
104 }
105 delete[] layers[i].neurons;
106 }
107 delete[] layers;
108 delete[] weightOffset;
109 delete[] biasOffset;
110 delete[] biasOnlyOffset;
111}
Neuron * neurons
Array of neurons in this layer.
double * weights
NN weights assigned to neuron.

References biasOffset, biasOnlyOffset, layers, nnp::NeuralNetwork::Layer::neurons, numLayers, nnp::NeuralNetwork::Layer::numNeurons, weightOffset, and nnp::NeuralNetwork::Neuron::weights.

Member Function Documentation

◆ setNormalizeNeurons()

void NeuralNetwork::setNormalizeNeurons ( bool  normalizeNeurons)

Turn on/off neuron normalization.

Parameters
[in]normalizeNeuronstrue or false (default: false).

Definition at line 113 of file NeuralNetwork.cpp.

114{
116
117 return;
118}

References normalizeNeurons.

◆ getNumNeurons()

int NeuralNetwork::getNumNeurons ( ) const

Return total number of neurons.

Includes input and output layer.

Definition at line 120 of file NeuralNetwork.cpp.

121{
122 int count = 0;
123
124 for (int i = 0; i < numLayers; i++)
125 {
126 count += layers[i].numNeurons;
127 }
128
129 return count;
130}

References layers, numLayers, and nnp::NeuralNetwork::Layer::numNeurons.

Referenced by getMemoryUsage().

Here is the caller graph for this function:

◆ getNumConnections()

int NeuralNetwork::getNumConnections ( ) const

Return total number of connections.

Connections are all weights and biases.

Definition at line 132 of file NeuralNetwork.cpp.

133{
134 return numConnections;
135}

References numConnections.

Referenced by nnp::Training::getWeights(), nnp::Training::randomizeNeuralNetworkWeights(), and nnp::Training::setWeights().

Here is the caller graph for this function:

◆ getNumWeights()

int NeuralNetwork::getNumWeights ( ) const

Return number of weights.

Definition at line 137 of file NeuralNetwork.cpp.

138{
139 return numWeights;
140}

References numWeights.

◆ getNumBiases()

int NeuralNetwork::getNumBiases ( ) const

Return number of biases.

Definition at line 142 of file NeuralNetwork.cpp.

143{
144 return numBiases;
145}

References numBiases.

◆ setConnections()

void NeuralNetwork::setConnections ( double const *const &  connections)

Set neural network weights and biases.

Parameters
[in]connectionsOne-dimensional array with neural network connections in the following order:

\[ \underbrace{ \overbrace{ a^{01}_{00}, \ldots, a^{01}_{0m_1}, a^{01}_{10}, \ldots, a^{01}_{1m_1}, \ldots, a^{01}_{n_00}, \ldots, a^{01}_{n_0m_1}, }^{\text{Weights}} \overbrace{ b^{1}_{0}, \ldots, b^{1}_{m_1} }^{\text{Biases}} }_{\text{Layer } 0 \rightarrow 1}, \underbrace{ a^{12}_{00}, \ldots, b^{2}_{m_2} }_{\text{Layer } 1 \rightarrow 2}, \ldots, \underbrace{ a^{p-1,p}_{00}, \ldots, b^{p}_{m_p} }_{\text{Layer } p-1 \rightarrow p} \]

where \(a^{i-1, i}_{jk}\) is the weight connecting neuron \(j\) in layer \(i-1\) to neuron \(k\) in layer \(i\) and \(b^{i}_{k}\) is the bias assigned to neuron \(k\) in layer \(i\).

Definition at line 147 of file NeuralNetwork.cpp.

148{
149 int count = 0;
150
151 for (int i = 1; i < numLayers; i++)
152 {
153 for (int j = 0; j < layers[i].numNeuronsPrevLayer; j++)
154 {
155 for (int k = 0; k < layers[i].numNeurons; k++)
156 {
157 layers[i].neurons[k].weights[j] = connections[count];
158 count++;
159 }
160 }
161 for (int j = 0; j < layers[i].numNeurons; j++)
162 {
163 layers[i].neurons[j].bias = connections[count];
164 count++;
165 }
166 }
167
168 return;
169}
double bias
Bias value assigned to this neuron (if this is neuron this bias value is ).

References nnp::NeuralNetwork::Neuron::bias, layers, nnp::NeuralNetwork::Layer::neurons, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, and nnp::NeuralNetwork::Neuron::weights.

Referenced by initializeConnectionsRandomUniform(), nnp::Training::randomizeNeuralNetworkWeights(), nnp::Mode::readNeuralNetworkWeights(), and nnp::Training::setWeights().

Here is the caller graph for this function:

◆ getConnections()

void NeuralNetwork::getConnections ( double *  connections) const

Get neural network weights and biases.

Parameters
[out]connectionsOne-dimensional array with neural network connections (same order as described in setConnections())

Definition at line 171 of file NeuralNetwork.cpp.

172{
173 int count = 0;
174
175 for (int i = 1; i < numLayers; i++)
176 {
177 for (int j = 0; j < layers[i].numNeuronsPrevLayer; j++)
178 {
179 for (int k = 0; k < layers[i].numNeurons; k++)
180 {
181 connections[count] = layers[i].neurons[k].weights[j] ;
182 count++;
183 }
184 }
185 for (int j = 0; j < layers[i].numNeurons; j++)
186 {
187 connections[count] = layers[i].neurons[j].bias;
188 count++;
189 }
190 }
191
192 return;
193}

References nnp::NeuralNetwork::Neuron::bias, layers, nnp::NeuralNetwork::Layer::neurons, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, and nnp::NeuralNetwork::Neuron::weights.

Referenced by nnp::Training::getWeights().

Here is the caller graph for this function:

◆ initializeConnectionsRandomUniform()

void NeuralNetwork::initializeConnectionsRandomUniform ( unsigned int  seed)

Initialize connections with random numbers.

Parameters
[in]seedRandom number generator seed.

Weights are initialized with random values in the \([-1, 1]\) interval. The C standard library rand() function is used.

Definition at line 195 of file NeuralNetwork.cpp.

196{
197 double* connections = new double[numConnections];
198
199 srand(seed);
200 for (int i = 0; i < numConnections; i++)
201 {
202 connections[i] = -1.0 + 2.0 * (double)rand() / RAND_MAX;
203 }
204
205 setConnections(connections);
206
207 delete[] connections;
208
209 return;
210}
void setConnections(double const *const &connections)
Set neural network weights and biases.

References numConnections, and setConnections().

Here is the call graph for this function:

◆ modifyConnections() [1/2]

void NeuralNetwork::modifyConnections ( ModificationScheme  modificationScheme)

Change connections according to a given modification scheme.

Parameters
[in]modificationSchemeDefines how the connections are modified. See ModificationScheme for possible options.

Definition at line 212 of file NeuralNetwork.cpp.

213{
214 if (modificationScheme == MS_ZEROBIAS)
215 {
216 for (int i = 0; i < numLayers; i++)
217 {
218 for (int j = 0; j < layers[i].numNeurons; j++)
219 {
220 layers[i].neurons[j].bias = 0.0;
221 }
222 }
223 }
224 else if (modificationScheme == MS_ZEROOUTPUTWEIGHTS)
225 {
226 for (int i = 0; i < outputLayer->numNeurons; i++)
227 {
228 for (int j = 0; j < outputLayer->numNeuronsPrevLayer; j++)
229 {
230 outputLayer->neurons[i].weights[j] = 0.0;
231 }
232 }
233 }
234 else if (modificationScheme == MS_FANIN)
235 {
236 for (int i = 1; i < numLayers; i++)
237 {
238 if(layers[i].activationFunction == AF_TANH)
239 {
240 for (int j = 0; j < layers[i].numNeurons; j++)
241 {
242 for (int k = 0; k < layers[i].numNeuronsPrevLayer; k++)
243 {
244 layers[i].neurons[j].weights[k] /=
245 sqrt(layers[i].numNeuronsPrevLayer);
246 }
247 }
248 }
249 }
250 }
251 else if (modificationScheme == MS_GLOROTBENGIO)
252 {
253 for (int i = 1; i < numLayers; i++)
254 {
255 if(layers[i].activationFunction == AF_TANH)
256 {
257 for (int j = 0; j < layers[i].numNeurons; j++)
258 {
259 for (int k = 0; k < layers[i].numNeuronsPrevLayer; k++)
260 {
261 layers[i].neurons[j].weights[k] *= sqrt(6.0 / (
262 layers[i].numNeuronsPrevLayer
263 + layers[i].numNeurons));
264 }
265 }
266 }
267 }
268 }
269 else if (modificationScheme == MS_NGUYENWIDROW)
270 {
271 double beta = 0.0;
272 double sum = 0.0;
273 double weight = 0.0;
274
275 for (int i = 1; i < numLayers-1; i++)
276 {
277 beta = 0.7 * pow(layers[i].numNeurons,
278 1.0 / double(layers[i].numNeuronsPrevLayer));
279 for (int j = 0; j < layers[i].numNeurons; j++)
280 {
281 sum = 0.0;
282 for (int k = 0; k < layers[i].numNeuronsPrevLayer; k++)
283 {
284 weight = layers[i].neurons[j].weights[k];
285 sum += weight * weight;
286 }
287 sum = sqrt(sum);
288 for (int k = 0; k < layers[i].numNeuronsPrevLayer; k++)
289 {
290 layers[i].neurons[j].weights[k] *= beta / sum;
291 if (layers[i].activationFunction == AF_TANH)
292 {
293 layers[i].neurons[j].weights[k] *= 2.0;
294 }
295 }
296 layers[i].neurons[j].bias *= beta;
297 if (layers[i].activationFunction == AF_TANH)
298 {
299 layers[i].neurons[j].bias *= 2.0;
300 }
301 }
302 }
303 for (int i = 0; i < outputLayer->numNeurons; i++)
304 {
305 outputLayer->neurons[0].weights[i] *= 0.5;
306 }
307 }
308 else
309 {
310 fprintf(stderr, "ERROR: Incorrect modifyConnections call.\n");
311 exit(EXIT_FAILURE);
312 }
313
314 return;
315}

References AF_TANH, nnp::NeuralNetwork::Neuron::bias, layers, MS_FANIN, MS_GLOROTBENGIO, MS_NGUYENWIDROW, MS_ZEROBIAS, MS_ZEROOUTPUTWEIGHTS, nnp::NeuralNetwork::Layer::neurons, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, outputLayer, and nnp::NeuralNetwork::Neuron::weights.

Referenced by nnp::Training::randomizeNeuralNetworkWeights().

Here is the caller graph for this function:

◆ modifyConnections() [2/2]

void NeuralNetwork::modifyConnections ( ModificationScheme  modificationScheme,
double  parameter1,
double  parameter2 
)

Change connections according to a given modification scheme.

Parameters
[in]modificationSchemeDefines how the connections are modified. See ModificationScheme for possible options.
[in]parameter1Additional parameter (see ModificationScheme).
[in]parameter2Additional parameter (see ModificationScheme).

Definition at line 317 of file NeuralNetwork.cpp.

320{
321 if (modificationScheme == MS_PRECONDITIONOUTPUT)
322 {
323 double mean = parameter1;
324 double sigma = parameter2;
325
326 for (int i = 0; i < outputLayer->numNeurons; i++)
327 {
328 for (int j = 0; j < outputLayer->numNeuronsPrevLayer; j++)
329 {
330 outputLayer->neurons[i].weights[j] *= sigma;
331 }
332 outputLayer->neurons[i].bias += mean;
333 }
334 }
335 else
336 {
337 fprintf(stderr, "ERROR: Incorrect modifyConnections call.\n");
338 exit(EXIT_FAILURE);
339 }
340
341 return;
342}

References nnp::NeuralNetwork::Neuron::bias, MS_PRECONDITIONOUTPUT, nnp::NeuralNetwork::Layer::neurons, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, outputLayer, and nnp::NeuralNetwork::Neuron::weights.

◆ setInput() [1/2]

void NeuralNetwork::setInput ( double const *const &  input) const

Set neural network input layer node values.

Parameters
[in]inputInput layer node values.

Definition at line 357 of file NeuralNetwork.cpp.

358{
359 for (int i = 0; i < inputLayer->numNeurons; i++)
360 {
361 // TODO: replace by calling setInput from above
362 // also, for 4G we need charges
363 double const& value = input[i];
364 Neuron& n = inputLayer->neurons[i];
365 n.count++;
366 n.value = value;
367 n.min = min(value, n.min);
368 n.max = max(value, n.max);
369 n.sum += value;
370 n.sum2 += value * value;
371 }
372
373 return;
374}
long count
How often the value of this neuron has been evaluated.

References nnp::NeuralNetwork::Neuron::count, inputLayer, nnp::NeuralNetwork::Neuron::max, nnp::NeuralNetwork::Neuron::min, nnp::NeuralNetwork::Layer::neurons, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Neuron::sum, nnp::NeuralNetwork::Neuron::sum2, and nnp::NeuralNetwork::Neuron::value.

Referenced by nnp::Mode::calculateAtomicNeuralNetworks(), nnp::Training::calculateWeightDerivatives(), nnp::Training::dPdc(), and nnp::Training::update().

Here is the caller graph for this function:

◆ setInput() [2/2]

void nnp::NeuralNetwork::setInput ( std::size_t const  index,
double const  value 
) const

Set neural network input layer node values.

Parameters
[in]indexIndex of neuron to set.
[in]valueInput layer neuron value.

◆ getOutput()

void NeuralNetwork::getOutput ( double *  output) const

Get neural network output layer node values.

Parameters
[out]outputOutput layer node values.

Definition at line 376 of file NeuralNetwork.cpp.

377{
378 for (int i = 0; i < outputLayer->numNeurons; i++)
379 {
380 output[i] = outputLayer->neurons[i].value;
381 }
382
383 return;
384}
double value
Neuron value.

References nnp::NeuralNetwork::Layer::neurons, nnp::NeuralNetwork::Layer::numNeurons, outputLayer, and nnp::NeuralNetwork::Neuron::value.

Referenced by nnp::Mode::calculateAtomicNeuralNetworks(), nnp::Training::calculateWeightDerivatives(), nnp::Training::dPdc(), and nnp::Training::update().

Here is the caller graph for this function:

◆ propagate()

void NeuralNetwork::propagate ( )

Propagate input information through all layers.

With the input data set by setInput() this will calculate all remaining neuron values, the output in the last layer is acccessible via getOutput().

Definition at line 386 of file NeuralNetwork.cpp.

387{
388 for (int i = 1; i < numLayers; i++)
389 {
390 propagateLayer(layers[i], layers[i-1]);
391 }
392
393 return;
394}
void propagateLayer(Layer &layer, Layer &layerPrev)
Propagate information from one layer to the next.

References layers, numLayers, and propagateLayer().

Referenced by nnp::Mode::calculateAtomicNeuralNetworks(), nnp::Training::calculateWeightDerivatives(), nnp::Training::dPdc(), and nnp::Training::update().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ calculateDEdG()

void NeuralNetwork::calculateDEdG ( double *  dEdG) const

Calculate derivative of output neuron with respect to input neurons.

Parameters
[out]dEdGArray containing derivative (length is number of input neurons).

CAUTION: This works only for neural networks with a single output neuron!

Returns \(\left(\frac{dE}{dG_i}\right)_{i=1,\ldots,N}\), where \(E\) is the output neuron and \(\left(G_i\right)_{i=1,\ldots,N}\) are the \(N\) input neurons.

Definition at line 396 of file NeuralNetwork.cpp.

397{
398 double** inner = new double*[numHiddenLayers];
399 double** outer = new double*[numHiddenLayers];
400
401 for (int i = 0; i < numHiddenLayers; i++)
402 {
403 inner[i] = new double[layers[i+1].numNeurons];
404 outer[i] = new double[layers[i+2].numNeurons];
405 }
406
407 for (int k = 0; k < layers[0].numNeurons; k++)
408 {
409 for (int i = 0; i < layers[1].numNeurons; i++)
410 {
411 inner[0][i] = layers[1].neurons[i].weights[k]
412 * layers[1].neurons[i].dfdx;
413 if (normalizeNeurons) inner[0][i] /= layers[0].numNeurons;
414 }
415 for (int l = 1; l < numHiddenLayers+1; l++)
416 {
417 for (int i2 = 0; i2 < layers[l+1].numNeurons; i2++)
418 {
419 outer[l-1][i2] = 0.0;
420 for (int i1 = 0; i1 < layers[l].numNeurons; i1++)
421 {
422 outer[l-1][i2] += layers[l+1].neurons[i2].weights[i1]
423 * inner[l-1][i1];
424 }
425 outer[l-1][i2] *= layers[l+1].neurons[i2].dfdx;
426 if (normalizeNeurons) outer[l-1][i2] /= layers[l].numNeurons;
427 if (l < numHiddenLayers) inner[l][i2] = outer[l-1][i2];
428 }
429 }
430 dEdG[k] = outer[numHiddenLayers-1][0];
431 }
432
433 for (int i = 0; i < numHiddenLayers; i++)
434 {
435 delete[] inner[i];
436 delete[] outer[i];
437 }
438 delete[] inner;
439 delete[] outer;
440
441 return;
442}
double dfdx
Derivative of activation function with respect to its argument .

References nnp::NeuralNetwork::Neuron::dfdx, layers, nnp::NeuralNetwork::Layer::neurons, normalizeNeurons, numHiddenLayers, nnp::NeuralNetwork::Layer::numNeurons, and nnp::NeuralNetwork::Neuron::weights.

Referenced by nnp::Mode::calculateAtomicNeuralNetworks(), nnp::Training::dPdc(), and nnp::Training::update().

Here is the caller graph for this function:

◆ calculateDEdc()

void NeuralNetwork::calculateDEdc ( double *  dEdc) const

Calculate derivative of output neuron with respect to connections.

Parameters
[out]dEdcArray containing derivative (length is number of connections, see getNumConnections()).

CAUTION: This works only for neural networks with a single output neuron!

Returns \(\left(\frac{dE}{dc_i}\right)_{i=1,\ldots,N}\), where \(E\) is the output neuron and \(\left(c_i\right)_{i=1,\ldots,N}\) are the \(N\) connections (weights and biases) of the neural network. See setConnections() for details on the order of weights an biases.

Definition at line 444 of file NeuralNetwork.cpp.

445{
446 int count = 0;
447
448 for (int i = 0; i < numConnections; i++)
449 {
450 dEdc[i] = 0.0;
451 }
452
453 for (int i = 0; i < outputLayer->numNeurons; i++)
454 {
457 {
458 dEdc[biasOffset[numLayers-2]+i] /=
460 }
461 }
462
463 for (int i = numLayers-2; i >= 0; i--)
464 {
465 count = 0;
466 for (int j = 0; j < layers[i].numNeurons; j++)
467 {
468 for (int k = 0; k < layers[i+1].numNeurons; k++)
469 {
470 dEdc[weightOffset[i]+count] = dEdc[biasOffset[i]+k]
471 * layers[i].neurons[j].value;
472 count++;
473 if (i >= 1)
474 {
475 dEdc[biasOffset[i-1]+j] += dEdc[biasOffset[i]+k]
476 * layers[i+1].neurons[k].weights[j]
477 * layers[i].neurons[j].dfdx;
478 }
479 }
480 if (normalizeNeurons && i >= 1)
481 {
482 dEdc[biasOffset[i-1]+j] /= layers[i].numNeuronsPrevLayer;
483 }
484 }
485 }
486
487 return;
488}

References biasOffset, nnp::NeuralNetwork::Neuron::dfdx, layers, nnp::NeuralNetwork::Layer::neurons, normalizeNeurons, numConnections, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, outputLayer, nnp::NeuralNetwork::Neuron::value, weightOffset, and nnp::NeuralNetwork::Neuron::weights.

Referenced by nnp::Training::calculateWeightDerivatives(), nnp::Training::dPdc(), and nnp::Training::update().

Here is the caller graph for this function:

◆ calculateDFdc()

void NeuralNetwork::calculateDFdc ( double *  dFdc,
double const *const &  dGdxyz 
) const

Calculate "second" derivative of output with respect to connections.

Parameters
[out]dFdcArray containing derivative (length is number of connections, see getNumConnections()).
[in]dGdxyzArray containing derivative of input neurons with respect to coordinates \(\frac{\partial G_j} {\partial x_{l, \gamma}}\).

CAUTION: This works only for neural networks with a single output neuron!

In the context of the neural network potentials this function is used to calculate derivatives of forces (or force contributions) with respect to connections. The force component \(\gamma\) (where \(\gamma\) is one of \(x,y,z\)) of particle \(l\) is

\[ F_{l, \gamma} = - \frac{\partial}{\partial x_{l, \gamma}} \sum_i^{N} E_i = - \sum_i^N \sum_j^M \frac{\partial E_i}{\partial G_j} \frac{\partial G_j}{\partial x_{l, \gamma}}, \]

where \(N\) is the number of particles in the system and \(M\) is the number of symmetry functions (number of input neurons). Hence the derivative of \(F_{l, \gamma}\) with respect to the neural network connection \(c_n\) is

\[ \frac{\partial}{\partial c_n} F_{l, \gamma} = - \sum_i^N \sum_j^M \frac{\partial^2 E_i}{\partial c_n \partial G_j} \frac{\partial G_j}{\partial x_{l, \gamma}}. \]

Thus, with given \(\frac{\partial G_j}{\partial x_{l, \gamma}}\) this function calculates

\[ \sum_j^M \frac{\partial^2 E}{\partial c_n \partial G_j} \frac{\partial G_j}{\partial x_{l, \gamma}} \]

for the current network status and returns it via the output array.

Definition at line 490 of file NeuralNetwork.cpp.

492{
493 double* dEdb = new double[numBiases];
494 double* d2EdGdc = new double[numConnections];
495
496 for (int i = 0; i < numBiases; i++)
497 {
498 dEdb[i] = 0.0;
499 }
500 for (int i = 0; i < numConnections; i++)
501 {
502 dFdc[i] = 0.0;
503 d2EdGdc[i] = 0.0;
504 }
505
506 calculateDEdb(dEdb);
507 for (int i = 0; i < layers[0].numNeurons; i++)
508 {
509 for (int j = 0; j < numConnections; j++)
510 {
511 d2EdGdc[j] = 0.0;
512 }
513 calculateDxdG(i);
514 calculateD2EdGdc(i, dEdb, d2EdGdc);
515 for (int j = 0; j < numConnections; j++)
516 {
517 // Note: F = - dE / dx !!
518 // ^
519 dFdc[j] -= d2EdGdc[j] * dGdxyz[i];
520 }
521 }
522
523 delete[] dEdb;
524 delete[] d2EdGdc;
525
526 return;
527}
void calculateDEdb(double *dEdb) const
Calculate derivative of output neuron with respect to biases.
void calculateD2EdGdc(int index, double const *const &dEdb, double *d2EdGdc) const
Calculate second derivative of output neuron with respect to input neuron and connections.
void calculateDxdG(int index) const
Calculate derivative of neuron values before activation function with respect to input neuron.

References calculateD2EdGdc(), calculateDEdb(), calculateDxdG(), layers, numBiases, numConnections, and nnp::NeuralNetwork::Layer::numNeurons.

Referenced by nnp::Training::calculateWeightDerivatives(), nnp::Training::dPdc(), and nnp::Training::update().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ writeConnections()

void NeuralNetwork::writeConnections ( std::ofstream &  file) const

Write connections to file.

Parameters
[in,out]fileFile stream to write to.

Definition at line 529 of file NeuralNetwork.cpp.

530{
531 // File header.
532 vector<string> title;
533 vector<string> colName;
534 vector<string> colInfo;
535 vector<size_t> colSize;
536 title.push_back("Neural network connection values (weights and biases).");
537 colSize.push_back(24);
538 colName.push_back("connection");
539 colInfo.push_back("Neural network connection value.");
540 colSize.push_back(1);
541 colName.push_back("t");
542 colInfo.push_back("Connection type (a = weight, b = bias).");
543 colSize.push_back(9);
544 colName.push_back("index");
545 colInfo.push_back("Index enumerating weights.");
546 colSize.push_back(5);
547 colName.push_back("l_s");
548 colInfo.push_back("Starting point layer (end point layer for biases).");
549 colSize.push_back(5);
550 colName.push_back("n_s");
551 colInfo.push_back("Starting point neuron in starting layer (end point "
552 "neuron for biases).");
553 colSize.push_back(5);
554 colName.push_back("l_e");
555 colInfo.push_back("End point layer.");
556 colSize.push_back(5);
557 colName.push_back("n_e");
558 colInfo.push_back("End point neuron in end layer.");
560 createFileHeader(title, colSize, colName, colInfo));
561
562 int count = 0;
563 for (int i = 1; i < numLayers; i++)
564 {
565 for (int j = 0; j < layers[i].numNeuronsPrevLayer; j++)
566 {
567 for (int k = 0; k < layers[i].numNeurons; k++)
568 {
569 count++;
570 file << strpr("%24.16E a %9d %5d %5d %5d %5d\n",
571 layers[i].neurons[k].weights[j],
572 count,
573 i - 1,
574 j + 1,
575 i,
576 k + 1);
577 }
578 }
579 for (int j = 0; j < layers[i].numNeurons; j++)
580 {
581 count++;
582 file << strpr("%24.16E b %9d %5d %5d\n",
583 layers[i].neurons[j].bias,
584 count,
585 i,
586 j + 1);
587 }
588 }
589
590 return;
591}
string strpr(const char *format,...)
String version of printf function.
Definition: utility.cpp:90
vector< string > createFileHeader(vector< string > const &title, vector< size_t > const &colSize, vector< string > const &colName, vector< string > const &colInfo, char const &commentChar)
Definition: utility.cpp:111
void appendLinesToFile(ofstream &file, vector< string > const lines)
Append multiple lines of strings to open file stream.
Definition: utility.cpp:225

References nnp::appendLinesToFile(), nnp::createFileHeader(), layers, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, and nnp::strpr().

Here is the call graph for this function:

◆ getNeuronStatistics()

void NeuralNetwork::getNeuronStatistics ( long *  count,
double *  min,
double *  max,
double *  sum,
double *  sum2 
) const

Return gathered neuron statistics.

Parameters
[out]countNumber of neuron output value calculations.
[out]minMinimum neuron value encountered.
[out]maxMaximum neuron value encountered.
[out]sumSum of all neuron values encountered.
[out]sum2Sum of squares of all neuron values encountered.

CAUTION: This works only for neural networks with a single output neuron!

When neuron values are calculated (e.g. when propagate() is called) statistics about encountered values are automatically gathered. The internal counters can be reset calling resetNeuronStatistics(). Neuron values are ordered layer by layer:

\[ \underbrace{y^0_1, \ldots, y^0_{N_0}}_\text{Input layer}, \underbrace{y^1_1, \ldots, y^1_{N_1}}_{\text{Hidden Layer } 1}, \ldots, \underbrace{y^p_1, \ldots, y^p_{N_p}}_\text{Output layer}, \]

where \(y^m_i\) is neuron \(i\) in layer \(m\) and \(N_m\) is the total number of neurons in this layer.

Definition at line 915 of file NeuralNetwork.cpp.

920{
921 int iNeuron = 0;
922
923 for (int i = 0; i < numLayers; i++)
924 {
925 for (int j = 0; j < layers[i].numNeurons; j++)
926 {
927 count[iNeuron] = layers[i].neurons[j].count;
928 min [iNeuron] = layers[i].neurons[j].min;
929 max [iNeuron] = layers[i].neurons[j].max;
930 sum [iNeuron] = layers[i].neurons[j].sum;
931 sum2 [iNeuron] = layers[i].neurons[j].sum2;
932 iNeuron++;
933 }
934 }
935
936 return;
937}
double max
Maximum neuron value over data set (neuron statistics).
double sum
Sum of neuron values over data set (neuron statistics).
double min
Minimum neuron value over data set (neuron statistics).
double sum2
Sum of squared neuron values over data set (neuron statistics).

References nnp::NeuralNetwork::Neuron::count, layers, nnp::NeuralNetwork::Neuron::max, nnp::NeuralNetwork::Neuron::min, nnp::NeuralNetwork::Layer::neurons, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Neuron::sum, and nnp::NeuralNetwork::Neuron::sum2.

◆ resetNeuronStatistics()

void NeuralNetwork::resetNeuronStatistics ( )

Reset neuron statistics.

Counters and summation variables for neuron statistics are reset.

Definition at line 898 of file NeuralNetwork.cpp.

899{
900 for (int i = 0; i < numLayers; i++)
901 {
902 for (int j = 0; j < layers[i].numNeurons; j++)
903 {
904 layers[i].neurons[j].count = 0;
905 layers[i].neurons[j].min = numeric_limits<double>::max();
906 layers[i].neurons[j].max = -numeric_limits<double>::max();
907 layers[i].neurons[j].sum = 0.0;
908 layers[i].neurons[j].sum2 = 0.0;
909 }
910 }
911
912 return;
913}

References nnp::NeuralNetwork::Neuron::count, layers, nnp::NeuralNetwork::Neuron::max, nnp::NeuralNetwork::Neuron::min, nnp::NeuralNetwork::Layer::neurons, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Neuron::sum, and nnp::NeuralNetwork::Neuron::sum2.

◆ getMemoryUsage()

long NeuralNetwork::getMemoryUsage ( )

Definition at line 981 of file NeuralNetwork.cpp.

982{
983 long mem = sizeof(*this);
984 int numNeurons = getNumNeurons();
985
986 mem += (numLayers - 1) * sizeof(int); // weightOffset
987 mem += (numLayers - 1) * sizeof(int); // biasOffset
988 mem += (numLayers - 1) * sizeof(int); // biasOnlyOffset
989 mem += numLayers * sizeof(Layer); // layers
990 mem += numNeurons * sizeof(Neuron); // neurons
991 mem += numWeights * sizeof(double); // weights
992
993 return mem;
994}
int getNumNeurons() const
Return total number of neurons.

References getNumNeurons(), numLayers, and numWeights.

Here is the call graph for this function:

◆ info()

vector< string > NeuralNetwork::info ( ) const

Print neural network architecture.

Definition at line 996 of file NeuralNetwork.cpp.

997{
998 vector<string> v;
999 int maxNeurons = 0;
1000
1001 v.push_back(strpr("Number of weights : %6zu\n", numWeights));
1002 v.push_back(strpr("Number of biases : %6zu\n", numBiases));
1003 v.push_back(strpr("Number of connections: %6zu\n", numConnections));
1004 v.push_back(strpr("Architecture "));
1005 for (int i = 0; i < numLayers; ++i)
1006 {
1007 maxNeurons = max(layers[i].numNeurons, maxNeurons);
1008 v.push_back(strpr(" %4d", layers[i].numNeurons));
1009 }
1010 v.push_back("\n");
1011 v.push_back("-----------------------------------------"
1012 "--------------------------------------\n");
1013
1014 for (int i = 0; i < maxNeurons; ++i)
1015 {
1016 v.push_back(strpr("%4d", i + 1));
1017 string s = "";
1018 for (int j = 0; j < numLayers; ++j)
1019 {
1020 if (i < layers[j].numNeurons)
1021 {
1022 if (j == 0)
1023 {
1024 s += strpr(" %3s", "G");
1025 }
1026 else if (layers[j].activationFunction == AF_IDENTITY)
1027 {
1028 s += strpr(" %3s", "l");
1029 }
1030 else if (layers[j].activationFunction == AF_TANH)
1031 {
1032 s += strpr(" %3s", "t");
1033 }
1034 else if (layers[j].activationFunction == AF_LOGISTIC)
1035 {
1036 s += strpr(" %3s", "s");
1037 }
1038 else if (layers[j].activationFunction == AF_SOFTPLUS)
1039 {
1040 s += strpr(" %3s", "p");
1041 }
1042 else if (layers[j].activationFunction == AF_RELU)
1043 {
1044 s += strpr(" %3s", "r");
1045 }
1046 else if (layers[j].activationFunction == AF_GAUSSIAN)
1047 {
1048 s += strpr(" %3s", "g");
1049 }
1050 else if (layers[j].activationFunction == AF_COS)
1051 {
1052 s += strpr(" %3s", "c");
1053 }
1054 else if (layers[j].activationFunction == AF_REVLOGISTIC)
1055 {
1056 s += strpr(" %3s", "S");
1057 }
1058 else if (layers[j].activationFunction == AF_EXP)
1059 {
1060 s += strpr(" %3s", "e");
1061 }
1062 else if (layers[j].activationFunction == AF_HARMONIC)
1063 {
1064 s += strpr(" %3s", "h");
1065 }
1066 }
1067 else
1068 {
1069 s += " ";
1070 }
1071 }
1072 v.push_back(s += "\n");
1073 }
1074
1075 return v;
1076}

References AF_COS, AF_EXP, AF_GAUSSIAN, AF_HARMONIC, AF_IDENTITY, AF_LOGISTIC, AF_RELU, AF_REVLOGISTIC, AF_SOFTPLUS, AF_TANH, layers, numBiases, numConnections, numLayers, numWeights, and nnp::strpr().

Here is the call graph for this function:

◆ calculateDEdb()

void NeuralNetwork::calculateDEdb ( double *  dEdb) const
private

Calculate derivative of output neuron with respect to biases.

Parameters
[out]dEdbArray containing derivatives (length is number of biases).

CAUTION: This works only for neural networks with a single output neuron!

Similar to calculateDEdc() but includes only biases. Used internally by calculateDFdc().

Definition at line 593 of file NeuralNetwork.cpp.

594{
595 for (int i = 0; i < outputLayer->numNeurons; i++)
596 {
599 {
600 dEdb[biasOnlyOffset[numLayers-2]+i] /=
602 }
603 }
604
605 for (int i = numLayers-2; i >= 0; i--)
606 {
607 for (int j = 0; j < layers[i].numNeurons; j++)
608 {
609 for (int k = 0; k < layers[i+1].numNeurons; k++)
610 {
611 if (i >= 1)
612 {
613 dEdb[biasOnlyOffset[i-1]+j] += dEdb[biasOnlyOffset[i]+k]
614 * layers[i+1].neurons[k].weights[j]
615 * layers[i].neurons[j].dfdx;
616 }
617 }
618 if (normalizeNeurons && i >= 1)
619 {
620 dEdb[biasOnlyOffset[i-1]+j] /= layers[i].numNeuronsPrevLayer;
621 }
622 }
623 }
624
625 return;
626}

References biasOnlyOffset, nnp::NeuralNetwork::Neuron::dfdx, layers, nnp::NeuralNetwork::Layer::neurons, normalizeNeurons, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, outputLayer, and nnp::NeuralNetwork::Neuron::weights.

Referenced by calculateDFdc().

Here is the caller graph for this function:

◆ calculateDxdG()

void NeuralNetwork::calculateDxdG ( int  index) const
private

Calculate derivative of neuron values before activation function with respect to input neuron.

Parameters
[in]indexIndex of input neuron the derivative will be calculated for.

No output, derivatives are internally saved for each neuron in Neuron::dxdG. Used internally by calculateDFdc().

Definition at line 628 of file NeuralNetwork.cpp.

629{
630 for (int i = 0; i < layers[1].numNeurons; i++)
631 {
632 layers[1].neurons[i].dxdG = layers[1].neurons[i].weights[index];
634 {
636 }
637 }
638 for (int i = 2; i < numLayers; i++)
639 {
640 for (int j = 0; j < layers[i].numNeurons; j++)
641 {
642 layers[i].neurons[j].dxdG = 0.0;
643 for (int k = 0; k < layers[i-1].numNeurons; k++)
644 {
645 layers[i].neurons[j].dxdG += layers[i].neurons[j].weights[k]
646 * layers[i-1].neurons[k].dfdx
647 * layers[i-1].neurons[k].dxdG;
648 }
650 {
652 }
653 }
654 }
655
656 return;
657}
double dxdG
Derivative of neuron value before application of activation function with respect to input layer neur...

References nnp::NeuralNetwork::Neuron::dfdx, nnp::NeuralNetwork::Neuron::dxdG, layers, nnp::NeuralNetwork::Layer::neurons, normalizeNeurons, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, and nnp::NeuralNetwork::Neuron::weights.

Referenced by calculateDFdc().

Here is the caller graph for this function:

◆ calculateD2EdGdc()

void NeuralNetwork::calculateD2EdGdc ( int  index,
double const *const &  dEdb,
double *  d2EdGdc 
) const
private

Calculate second derivative of output neuron with respect to input neuron and connections.

Parameters
[in]indexIndex of input neuron the derivative will be calculated for.
[in]dEdbDerivatives of output neuron with respect to biases. See calculateDEdb().
[out]d2EdGdcArray containing the derivatives (ordered as described in setConnections()).

CAUTION: This works only for neural networks with a single output neuron!

Used internally by calculateDFdc().

Definition at line 659 of file NeuralNetwork.cpp.

662{
663 int count = 0;
664
665 for (int i = 0; i < outputLayer->numNeurons; i++)
666 {
667 d2EdGdc[biasOffset[numLayers-2]+i] = outputLayer->neurons[i].d2fdx2
670 {
671 d2EdGdc[biasOffset[numLayers-2]+i] /=
673 }
674 }
675
676 for (int i = numLayers-2; i >= 0; i--)
677 {
678 count = 0;
679 for (int j = 0; j < layers[i].numNeurons; j++)
680 {
681 for (int k = 0; k < layers[i+1].numNeurons; k++)
682 {
683 if (i == 0)
684 {
685 d2EdGdc[weightOffset[i]+count] =
686 d2EdGdc[biasOffset[i]+k] * layers[i].neurons[j].value;
687 if (j == index)
688 {
689 d2EdGdc[weightOffset[i]+count] +=
690 dEdb[biasOnlyOffset[i]+k];
691 }
692 }
693 else
694 {
695 d2EdGdc[weightOffset[i]+count] =
696 d2EdGdc[biasOffset[i]+k] * layers[i].neurons[j].value
697 + dEdb[biasOnlyOffset[i]+k] * layers[i].neurons[j].dfdx
698 * layers[i].neurons[j].dxdG;
699 }
700 count++;
701 if (i >= 1)
702 {
703 d2EdGdc[biasOffset[i-1]+j] +=
704 layers[i+1].neurons[k].weights[j]
705 * (d2EdGdc[biasOffset[i]+k] * layers[i].neurons[j].dfdx
706 + dEdb[biasOnlyOffset[i]+k]
707 * layers[i].neurons[j].d2fdx2
708 * layers[i].neurons[j].dxdG);
709 }
710 }
711 if (normalizeNeurons && i >= 1)
712 {
713 d2EdGdc[biasOffset[i-1]+j] /= layers[i].numNeuronsPrevLayer;
714 }
715 }
716 }
717
718 return;
719}
double d2fdx2
Second derivative of activation function with respect to its argument .

References biasOffset, biasOnlyOffset, nnp::NeuralNetwork::Neuron::d2fdx2, nnp::NeuralNetwork::Neuron::dfdx, nnp::NeuralNetwork::Neuron::dxdG, layers, nnp::NeuralNetwork::Layer::neurons, normalizeNeurons, numLayers, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, outputLayer, nnp::NeuralNetwork::Neuron::value, weightOffset, and nnp::NeuralNetwork::Neuron::weights.

Referenced by calculateDFdc().

Here is the caller graph for this function:

◆ allocateLayer()

void NeuralNetwork::allocateLayer ( Layer layer,
int  numNeuronsPrevLayer,
int  numNeurons,
ActivationFunction  activationFunction 
)
private

Allocate a single layer.

Parameters
[in,out]layerNeural network layer to allocate.
[in]numNeuronsPrevLayerNumber of neurons in the previous layer.
[in]numNeuronsNumber of neurons in this layer.
[in]activationFunctionActivation function to use for all neurons in this layer.

This function is internally called by the constructor to allocate all neurons layer by layer.

Definition at line 721 of file NeuralNetwork.cpp.

725{
726 layer.numNeurons = numNeurons;
727 layer.numNeuronsPrevLayer = numNeuronsPrevLayer;
728 layer.activationFunction = activationFunction;
729
730 layer.neurons = new Neuron[layer.numNeurons];
731 for (int i = 0; i < layer.numNeurons; i++)
732 {
733 layer.neurons[i].x = 0.0;
734 layer.neurons[i].value = 0.0;
735 layer.neurons[i].dfdx = 0.0;
736 layer.neurons[i].d2fdx2 = 0.0;
737 layer.neurons[i].bias = 0.0;
738 layer.neurons[i].dxdG = 0.0;
739 layer.neurons[i].count = 0;
740 layer.neurons[i].min = numeric_limits<double>::max();
741 layer.neurons[i].max = -numeric_limits<double>::max();
742 layer.neurons[i].sum = 0.0;
743 layer.neurons[i].sum2 = 0.0;
744 if (layer.numNeuronsPrevLayer > 0)
745 {
746 layer.neurons[i].weights = new double[layer.numNeuronsPrevLayer];
747 for (int j = 0; j < layer.numNeuronsPrevLayer; j++)
748 {
749 layer.neurons[i].weights[j] = 0.0;
750 }
751 }
752 else
753 {
754 layer.neurons[i].weights = 0;
755 }
756 }
757
758 return;
759}

References nnp::NeuralNetwork::Layer::activationFunction, nnp::NeuralNetwork::Neuron::bias, nnp::NeuralNetwork::Neuron::count, nnp::NeuralNetwork::Neuron::d2fdx2, nnp::NeuralNetwork::Neuron::dfdx, nnp::NeuralNetwork::Neuron::dxdG, nnp::NeuralNetwork::Neuron::max, nnp::NeuralNetwork::Neuron::min, nnp::NeuralNetwork::Layer::neurons, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, nnp::NeuralNetwork::Neuron::sum, nnp::NeuralNetwork::Neuron::sum2, nnp::NeuralNetwork::Neuron::value, nnp::NeuralNetwork::Neuron::weights, and nnp::NeuralNetwork::Neuron::x.

Referenced by NeuralNetwork().

Here is the caller graph for this function:

◆ propagateLayer()

void NeuralNetwork::propagateLayer ( Layer layer,
Layer layerPrev 
)
private

Propagate information from one layer to the next.

Parameters
[in,out]layerNeuron values in this layer will be calculated.
[in]layerPrevNeuron values in this layer will be used as input.

This function is internally looped by propagate().

Definition at line 761 of file NeuralNetwork.cpp.

762{
763 double dtmp = 0.0;
764
765 for (int i = 0; i < layer.numNeurons; i++)
766 {
767 dtmp = 0.0;
768 for (int j = 0; j < layer.numNeuronsPrevLayer; j++)
769 {
770 dtmp += layer.neurons[i].weights[j] * layerPrev.neurons[j].value;
771 }
772 dtmp += layer.neurons[i].bias;
774 {
775 dtmp /= layer.numNeuronsPrevLayer;
776 }
777
778 layer.neurons[i].x = dtmp;
779 if (layer.activationFunction == AF_IDENTITY)
780 {
781 layer.neurons[i].value = dtmp;
782 layer.neurons[i].dfdx = 1.0;
783 layer.neurons[i].d2fdx2 = 0.0;
784 }
785 else if (layer.activationFunction == AF_TANH)
786 {
787 dtmp = tanh(dtmp);
788 layer.neurons[i].value = dtmp;
789 layer.neurons[i].dfdx = 1.0 - dtmp * dtmp;
790 layer.neurons[i].d2fdx2 = -2.0 * dtmp * (1.0 - dtmp * dtmp);
791 }
792 else if (layer.activationFunction == AF_LOGISTIC)
793 {
794 if (dtmp > EXP_LIMIT)
795 {
796 layer.neurons[i].value = 1.0;
797 layer.neurons[i].dfdx = 0.0;
798 layer.neurons[i].d2fdx2 = 0.0;
799 }
800 else if (dtmp < -EXP_LIMIT)
801 {
802 layer.neurons[i].value = 0.0;
803 layer.neurons[i].dfdx = 0.0;
804 layer.neurons[i].d2fdx2 = 0.0;
805 }
806 else
807 {
808 dtmp = 1.0 / (1.0 + exp(-dtmp));
809 layer.neurons[i].value = dtmp;
810 layer.neurons[i].dfdx = dtmp * (1.0 - dtmp);
811 layer.neurons[i].d2fdx2 = dtmp * (1.0 - dtmp)
812 * (1.0 - 2.0 * dtmp);
813 }
814 }
815 else if (layer.activationFunction == AF_SOFTPLUS)
816 {
817 if (dtmp > EXP_LIMIT)
818 {
819 layer.neurons[i].value = dtmp;
820 layer.neurons[i].dfdx = 1.0;
821 layer.neurons[i].d2fdx2 = 0.0;
822 }
823 else if (dtmp < -EXP_LIMIT)
824 {
825 layer.neurons[i].value = 0.0;
826 layer.neurons[i].dfdx = 0.0;
827 layer.neurons[i].d2fdx2 = 0.0;
828 }
829 else
830 {
831 dtmp = exp(dtmp);
832 layer.neurons[i].value = log(1.0 + dtmp);
833 dtmp = 1.0 / (1.0 + 1.0 / dtmp);
834 layer.neurons[i].dfdx = dtmp;
835 layer.neurons[i].d2fdx2 = dtmp * (1.0 - dtmp);
836 }
837 }
838 else if (layer.activationFunction == AF_RELU)
839 {
840 if (dtmp > 0.0)
841 {
842 layer.neurons[i].value = dtmp;
843 layer.neurons[i].dfdx = 1.0;
844 layer.neurons[i].d2fdx2 = 0.0;
845 }
846 else
847 {
848 layer.neurons[i].value = 0.0;
849 layer.neurons[i].dfdx = 0.0;
850 layer.neurons[i].d2fdx2 = 0.0;
851 }
852 }
853 else if (layer.activationFunction == AF_GAUSSIAN)
854 {
855 double const tmpexp = exp(-0.5 * dtmp * dtmp);
856 layer.neurons[i].value = tmpexp;
857 layer.neurons[i].dfdx = -dtmp * tmpexp;
858 layer.neurons[i].d2fdx2 = (dtmp * dtmp - 1.0) * tmpexp;
859 }
860 else if (layer.activationFunction == AF_COS)
861 {
862 double const tmpcos = cos(dtmp);
863 layer.neurons[i].value = tmpcos;
864 layer.neurons[i].dfdx = -sin(dtmp);
865 layer.neurons[i].d2fdx2 = -tmpcos;
866 }
867 else if (layer.activationFunction == AF_REVLOGISTIC)
868 {
869 dtmp = 1.0 / (1.0 + exp(-dtmp));
870 layer.neurons[i].value = 1.0 - dtmp;
871 layer.neurons[i].dfdx = dtmp * (dtmp - 1.0);
872 layer.neurons[i].d2fdx2 = dtmp * (dtmp - 1.0) * (1.0 - 2.0 * dtmp);
873 }
874 else if (layer.activationFunction == AF_EXP)
875 {
876 dtmp = exp(-dtmp);
877 layer.neurons[i].value = dtmp;
878 layer.neurons[i].dfdx = -dtmp;
879 layer.neurons[i].d2fdx2 = dtmp;
880 }
881 else if (layer.activationFunction == AF_HARMONIC)
882 {
883 layer.neurons[i].value = dtmp * dtmp;
884 layer.neurons[i].dfdx = 2.0 * dtmp;
885 layer.neurons[i].d2fdx2 = 2.0;
886 }
887 layer.neurons[i].count++;
888 dtmp = layer.neurons[i].x;
889 layer.neurons[i].min = min(dtmp, layer.neurons[i].min);
890 layer.neurons[i].max = max(dtmp, layer.neurons[i].max);
891 layer.neurons[i].sum += dtmp;
892 layer.neurons[i].sum2 += dtmp * dtmp;
893 }
894
895 return;
896}
#define EXP_LIMIT

References nnp::NeuralNetwork::Layer::activationFunction, AF_COS, AF_EXP, AF_GAUSSIAN, AF_HARMONIC, AF_IDENTITY, AF_LOGISTIC, AF_RELU, AF_REVLOGISTIC, AF_SOFTPLUS, AF_TANH, nnp::NeuralNetwork::Neuron::bias, nnp::NeuralNetwork::Neuron::count, nnp::NeuralNetwork::Neuron::d2fdx2, nnp::NeuralNetwork::Neuron::dfdx, EXP_LIMIT, nnp::NeuralNetwork::Neuron::max, nnp::NeuralNetwork::Neuron::min, nnp::NeuralNetwork::Layer::neurons, normalizeNeurons, nnp::NeuralNetwork::Layer::numNeurons, nnp::NeuralNetwork::Layer::numNeuronsPrevLayer, nnp::NeuralNetwork::Neuron::sum, nnp::NeuralNetwork::Neuron::sum2, nnp::NeuralNetwork::Neuron::value, nnp::NeuralNetwork::Neuron::weights, and nnp::NeuralNetwork::Neuron::x.

Referenced by propagate().

Here is the caller graph for this function:

Member Data Documentation

◆ normalizeNeurons

bool nnp::NeuralNetwork::normalizeNeurons
private

◆ numWeights

int nnp::NeuralNetwork::numWeights
private

Number of NN weights only.

Definition at line 409 of file NeuralNetwork.h.

Referenced by getMemoryUsage(), getNumWeights(), info(), and NeuralNetwork().

◆ numBiases

int nnp::NeuralNetwork::numBiases
private

Number of NN biases only.

Definition at line 411 of file NeuralNetwork.h.

Referenced by calculateDFdc(), getNumBiases(), info(), and NeuralNetwork().

◆ numConnections

int nnp::NeuralNetwork::numConnections
private

Number of NN connections (weights + biases).

Definition at line 413 of file NeuralNetwork.h.

Referenced by calculateDEdc(), calculateDFdc(), getNumConnections(), info(), initializeConnectionsRandomUniform(), and NeuralNetwork().

◆ numLayers

int nnp::NeuralNetwork::numLayers
private

◆ numHiddenLayers

int nnp::NeuralNetwork::numHiddenLayers
private

Number of hidden layers.

Definition at line 417 of file NeuralNetwork.h.

Referenced by calculateDEdG(), and NeuralNetwork().

◆ weightOffset

int* nnp::NeuralNetwork::weightOffset
private

Offset adress of weights per layer in combined weights+bias array.

Definition at line 419 of file NeuralNetwork.h.

Referenced by calculateD2EdGdc(), calculateDEdc(), NeuralNetwork(), and ~NeuralNetwork().

◆ biasOffset

int* nnp::NeuralNetwork::biasOffset
private

Offset adress of biases per layer in combined weights+bias array.

Definition at line 421 of file NeuralNetwork.h.

Referenced by calculateD2EdGdc(), calculateDEdc(), NeuralNetwork(), and ~NeuralNetwork().

◆ biasOnlyOffset

int* nnp::NeuralNetwork::biasOnlyOffset
private

Offset adress of biases per layer in bias only array.

Definition at line 423 of file NeuralNetwork.h.

Referenced by calculateD2EdGdc(), calculateDEdb(), NeuralNetwork(), and ~NeuralNetwork().

◆ inputLayer

Layer* nnp::NeuralNetwork::inputLayer
private

Pointer to input layer.

Definition at line 425 of file NeuralNetwork.h.

Referenced by NeuralNetwork(), and setInput().

◆ outputLayer

Layer* nnp::NeuralNetwork::outputLayer
private

Pointer to output layer.

Definition at line 427 of file NeuralNetwork.h.

Referenced by calculateD2EdGdc(), calculateDEdb(), calculateDEdc(), getOutput(), modifyConnections(), and NeuralNetwork().

◆ layers


The documentation for this class was generated from the following files: