// Copyright 2008 Google Inc.
// All Rights Reserved.
// Author: ahmadab@google.com (Ahmad Abdulkader)
//
// neuron.cpp: The implementation of a class for an object
// that represents a single neuron in a neural network
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "neuron.h"
#include "input_file_buffer.h"

namespace tesseract {

// Instantiate all supported templates
template bool Neuron::ReadBinary(InputFileBuffer *input_buffer);

// default and only constructor
Neuron::Neuron() {
  Init();
}

// virtual destructor
Neuron::~Neuron() {
}

// Initializer
void Neuron::Init() {
  id_ = -1;
  frwd_dirty_ = false;
  fan_in_.clear();
  fan_in_weights_.clear();
  activation_ = 0.0f;
  output_ = 0.0f;
  bias_ = 0.0f;
  node_type_ = Unknown;
}

// Computes the activation and output of the neuron if not fresh
// by pulling the outputs of all fan-in neurons
void Neuron::FeedForward() {
  if (!frwd_dirty_ ) {
    return;
  }
  // nothing to do for input nodes: just pass the input to the o/p
  // otherwise, pull the output of all fan-in neurons
  if (node_type_ != Input) {
    int fan_in_cnt = fan_in_.size();
    // sum out the activation
    activation_ = -bias_;
    for (int in = 0; in < fan_in_cnt; in++) {
      if (fan_in_[in]->frwd_dirty_) {
        fan_in_[in]->FeedForward();
      }
      activation_ += ((*(fan_in_weights_[in])) * fan_in_[in]->output_);
    }
    // sigmoid it
    output_ = Sigmoid(activation_);
  }
  frwd_dirty_ = false;
}

// set the type of the neuron
void Neuron::set_node_type(NeuronTypes Type) {
  node_type_ = Type;
}

// Adds new connections *to* this neuron *From*
// a target neuron using specfied params
// Note that what is actually copied in this function are pointers to the
// specified Neurons and weights and not the actualt values. This is by
// design to centralize the alloction of neurons and weights and so
// increase the locality of reference and improve cache-hits resulting
// in a faster net. This technique resulted in a 2X-10X speedup
// (depending on network size and processor)
void Neuron::AddFromConnection(Neuron *neurons,
                               float *wts_offset,
                               int from_cnt) {
  for (int in = 0; in < from_cnt; in++) {
    fan_in_.push_back(neurons + in);
    fan_in_weights_.push_back(wts_offset + in);
  }
}

// fast computation of sigmoid function using a lookup table
// defined in sigmoid_table.cpp
float Neuron::Sigmoid(float activation) {
  if (activation <= -10.0f) {
    return 0.0f;
  } else if (activation >= 10.0f) {
    return 1.0f;
  } else {
    return kSigmoidTable[static_cast<int>(100 * (activation + 10.0))];
  }
}
}
