//////////////////////////////////////////////////////////////////////////////
//
// File: nn_arch.cc
//
// Purpose: Interface for NeuNet's Arch class.
//
// Authors:
//   txe  Travis Emmitt
//
// Modifications:
//   19-APR-1998  txe  Initial creation
//   20-APR-1998  txe  Ported to UNIX, added Copy()
//   21-APR-1998  txe  Added loop to Mutate()
//   23-APR-1998  txe  Using static debug (constructor changed)
//   24-APR-1998  txe  Improved output
//
//////////////////////////////////////////////////////////////////////////////

#include <fstream.h>
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "nn_arch.h"

//////////////////////////////////////////////////////////////////////////////

Arch::Arch (char *name)
    : Debug (name) {

  act_add       = DEFAULT_ACT_ADD;
  act_numerator = DEFAULT_ACT_NUMERATOR;
  encouragement = DEFAULT_ENCOURAGEMENT;
  learning_rate = DEFAULT_LEARNING_RATE;
  max_weight    = DEFAULT_MAX_WEIGHT;
  min_weight    = DEFAULT_MIN_WEIGHT;
  momentum      = DEFAULT_MOMENTUM;
  num_layers    = 0;
}

//////////////////////////////////////////////////////////////////////////////

Arch::~Arch () {
  DEBUG(DEL) << "Destroying " << name << "\n";
}

//////////////////////////////////////////////////////////////////////////////

void Arch::Copy (Arch *arch) {
  ASSERT (arch != NULL);

  learning_rate = arch->learning_rate;
  momentum      = arch->momentum;
  encouragement = arch->encouragement;
  act_numerator = arch->act_numerator;
  act_add       = arch->act_add;
  min_weight    = arch->min_weight;
  max_weight    = arch->max_weight;
  num_layers    = arch->num_layers;

  for (int i = 0; i < arch->num_layers; i++) {
    num_nodes[i] = arch->num_nodes[i];
  }  
}

//////////////////////////////////////////////////////////////////////////////

int Arch::Load (char *filename) {
  ASSERT (filename != NULL);

  char line[LINE_LEN+1];
  int i, num_hidden_layers;
  FILE *fp;

  DEBUG(1) << name << " loading configuration from '" << filename << "'...\n";

  if ((fp = fopen (filename, "rt")) == NULL) {
    ERR << "Couldn't open configuration file '" << filename << "'\n";
    return 0;
  }

  SKIP_COMMENTS (fp, line);
  sscanf (line, "%d", &num_layers);

  num_layers = MAX (2, MIN (MAX_LAYERS, num_layers + 2));

  for (i = 1; i < num_layers - 1; i++) {
    SKIP_COMMENTS (fp, line);
    sscanf (line, "%d", &num_nodes[i]);
  }

  num_nodes[0] = 0;     // no count of input nodes yet
  num_nodes[i] = 0;     // no count of output nodes yet

  SKIP_COMMENTS (fp, line);
  sscanf (line, "%f", &learning_rate);
  SKIP_COMMENTS (fp, line);
  sscanf (line, "%f", &momentum);
  SKIP_COMMENTS (fp, line);
  sscanf (line, "%f", &encouragement);
  SKIP_COMMENTS (fp, line);
  sscanf (line, "%f", &min_weight);
  SKIP_COMMENTS (fp, line);
  sscanf (line, "%f", &max_weight);
  SKIP_COMMENTS (fp, line);
  sscanf (line, "%f", &act_numerator);
  SKIP_COMMENTS (fp, line);
  sscanf (line, "%f", &act_add);

  fclose (fp);
  return 1;
}

//////////////////////////////////////////////////////////////////////////////

int Arch::Mutate (float mutation_rate) {
  DEBUG(MUT) << "\n" << name << " mutating: ";

  int mutations = 0;

  for (int i = 0; i < MAX_MUTATION_ATTEMPTS && !mutations; i++) {
    if (F_random (1) < mutation_rate * 2) {
      DEBUG(MUT) << " [# hidden nodes]";
      for (int j = 1; j < num_layers - 1; j++) {
	num_nodes[j] += (int) R_random (10);
	num_nodes[j] = MAX (1, num_nodes[j]);
      }
      mutations++;
    }
    
    if (F_random (1) < mutation_rate) {
      DEBUG(MUT) << " [learning rate]";
      learning_rate += R_random (.3);
      mutations++;
    }
    
    if (F_random (1) < mutation_rate) {
      DEBUG(MUT) << " [momentum]";
      momentum += R_random (.3);
      mutations++;
    }
    
    if (F_random (1) < mutation_rate) {
      DEBUG(MUT) << " [encouragement]";
      encouragement += R_random (.1);
      mutations++;
    }
    
    if (F_random (1) < mutation_rate) {
      DEBUG(MUT) << " [min link wt]";
      min_weight += R_random (2);
      mutations++;
    }
    
    if (F_random (1) < mutation_rate) {
      DEBUG(MUT) << " [max link wt]";
      max_weight += R_random (2);
      mutations++;
    }
    
////////////////////////////////////////////////////////////////
// don't mutate the activation function parameters b/c it'd
// allow the level to exceed 1.0, which screws everything up
///////////////////////////////////////////////////////////////
    
/*
    if (F_random (1) < mutation_rate) {
      DEBUG(MUT) << "  mutating activation function numerator...\n";
      act_numerator += R_random (1);
      mutations++;
    }

    if (F_random (1) < mutation_rate) {
      DEBUG(MUT) << "  mutating activation function add...\n";
      act_add += R_random (1);
      mutations++;
    }
*/
  }

  DEBUG(MUT) << "\n";

  if (!mutations) {
    WARN << name << " failed to mutate even after "
	 << MAX_MUTATION_ATTEMPTS << " attempts\n";
  }

  return mutations;
}


//////////////////////////////////////////////////////////////////////////////

void Arch::Print () {
  cout << "\n" << name << " configuration:"
       << "\n  " << num_layers - 2 << " hidden layers:";

  for (int i = 1; i < num_layers - 1; i++) {
    cout << "\n    layer[" << i << "] has " << num_nodes[i] << " nodes";
  }

  cout << "\n  Learning Rate  = " << learning_rate
       << "\n  Momentum       = " << momentum
       << "\n  Encouragement  = " << encouragement
       << "\n  Minimum weight = " << min_weight
       << "\n  Maximum weight = " << max_weight
       << "\n  Act numerator  = " << act_numerator
       << "\n  Act add        = " << act_add
       << "\n\n";
}

//////////////////////////////////////////////////////////////////////////////

void Arch::Print (int level) {
  if (debug >= level) {
    Print ();
  }
}

//////////////////////////////////////////////////////////////////////////////

int Arch::Save (char *filename) {
  ASSERT (filename != NULL);

  char line[LINE_LEN+1];
  ofstream out;

  DEBUG(1) << name << " saving configuration to '" << filename << "'...\n";

  out.open (filename);
  if (!out) {
    ERR << "Couldn't open configuration file '" << filename << "'\n";
    return 0;
  }

  out << "/////////////////////////////////////////////////////////////////////\n"
      << "//\n"
      << "// " << filename << "\n"
      << "//\n"
      << "// Saved configuration for " << name << "\n"
      << "//\n"
      << "/////////////////////////////////////////////////////////////////////\n"
      << "\n"
      << "// Number of hidden layers ////////\n\n" << num_layers - 2;
  
  for (int i = 1; i < num_layers - 1; i++) {
    out << "\n\n// Number of nodes for layer " << i << " //\n\n"
	<< num_nodes[i];
  }

  out << "\n\n// Learning rate //////////////////\n\n" << learning_rate
      << "\n\n// Momentum ///////////////////////\n\n" << momentum
      << "\n\n// Encouragement //////////////////\n\n" << encouragement
      << "\n\n// Minimum (Initial) Link Weight //\n\n" << min_weight
      << "\n\n// Maximum (Initial) Link Weight //\n\n" << max_weight
      << "\n\n// Activation function numerator //\n\n" << act_numerator
      << "\n\n// Activation function add ////////\n\n" << act_add
      << "\n";
  
  out.close ();
  return 1;
}

//////////////////////////////////////////////////////////////////////////////

int Arch::Valid () {
  DEBUG(3) << "Validating " << name << "...\n";

  if (num_layers < 0 || num_layers > MAX_LAYERS) {
    ERR << name << " invalid: num_layers = " << num_layers << "\n";
    return 0;
  }

  for (int i = 0; i < num_layers; i++) {
    if (num_nodes[i] < 1 || num_nodes[i] > MAX_NODES) {
      ERR << name << " invalid: num_nodes[" << i << "] = "
	  << num_nodes[i] << "\n";
      return 0;
    }
  }

  return 1;
}

//////////////////////////////////////////////////////////////////////////////

