// Copyright (C) 2000 Dominic Letourneau (doumdi@yahoo.com)

// FuzzyModel.cc: implementation of the FuzzyModel class.
//
//////////////////////////////////////////////////////////////////////

#include "FuzzyModel.h"
#include "Exception.h"
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <iostream>

using namespace std;

namespace FD {

//////////////////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////////////////
const int FuzzyModel::FUZZY_INPUT_SET = 0;
const int FuzzyModel::FUZZY_OUTPUT_SET = 1;

//////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////
FuzzyModel::FuzzyModel()
  : BufferedNode("INVALID",ParameterSet()) {

}

FuzzyModel::FuzzyModel(const FuzzyModel &model)
  : BufferedNode("INVALID",ParameterSet()) {

  //inserting input set
  //cerr<<"copying input set"<<endl;
  for (int i = 0; i < model.m_input_set.size(); i++) {
    m_input_set.push_back(model.m_input_set[i]->clone());
  }

  //inserting output set
  //cerr<<"copying output set"<<endl;
  for (int i = 0; i < model.m_output_set.size(); i++) {
    m_output_set.push_back(model.m_output_set[i]->clone());
  }
  
  //readding rules
  //cerr<<"copying rules"<<endl;
  for (int i = 0; i < model.m_rules.size(); i++) {
    add_fuzzy_rule(model.m_rules[i]->clone());
  }

}


FuzzyModel::FuzzyModel(string nodeName, ParameterSet params)
  : BufferedNode(nodeName,params) {

  m_RuleID = addInput("RULES");;
  m_ASetID = addInput("ANTECEDENT_SETS");
  m_CSetID = addInput("CONSEQUENT_SETS");
  m_InputID = addInput("INPUT");
  m_OutputID = addOutput("OUTPUT");
  m_ModelID = addOutput("MODEL");

}
//////////////////////////////////////////////////////////////////////
// Destruction
//////////////////////////////////////////////////////////////////////
FuzzyModel::~FuzzyModel() {

  int i;
 
  //resizing vectors
  m_rules.resize(0);
  m_input_set.resize(0);
  m_output_set.resize(0);
  
}
//////////////////////////////////////////////////////////////////////
// Add a fuzzy rule to the model
//////////////////////////////////////////////////////////////////////
void FuzzyModel::add_fuzzy_rule(ObjectRef ruleValue) {

  FuzzySet *set = NULL;
  FuzzyFunction *function = NULL;
  FuzzyRule &rule = object_cast<FuzzyRule>(ruleValue);  
  
  int i;
  
  //int rule_number = rule->get_rule_number();
  int rule_number = m_rules.size() + 1;

  rule.set_rule_number(rule_number);
  
  //rule->print_rule(cerr);


  //let's verify this rule
  vector<pair<string,string> >&consequent = rule.get_consequent_part();
  vector<pair<string,string> >&antecedant = rule.get_antecedant_part();
  
  
  //rule number verification
  if (m_input_functions.size() < rule_number) {
    m_input_functions.resize(rule_number);
  }
  else {
    if (!m_input_functions[rule_number -1].empty()) {
      char message[256];	
      sprintf(message,"RULE %i ALREADY EXISTS",rule_number);
      throw new GeneralException(message,__FILE__,__LINE__);
    }
  }

  if (m_output_functions.size() < rule_number) {
    m_output_functions.resize(rule_number);
  }
  else {
    if (!m_output_functions[rule_number -1].empty()) {
      char message[256];	
      sprintf(message,"RULE %i ALREADY EXISTS",rule_number);
      throw new GeneralException(message,__FILE__,__LINE__);
    }
  }
  
  
  //antecedant verification
  for (i = 0; i < antecedant.size(); i++) {
    
    set = find_set_named(antecedant[i].first, FuzzyModel::FUZZY_INPUT_SET);
    
    if (!set) {
      char message[256];
      sprintf(message,"SET NOT FOUND (%s)",antecedant[i].first.c_str());
      throw new GeneralException(message,__FILE__,__LINE__);
    }
    else {

      function = set->find_function_by_name(antecedant[i].second);
      
      
      if (!function) {
	char message[256];
	sprintf(message,"SET VARIABLE NOT FOUND (%s)(%s)",
		antecedant[i].first.c_str(),antecedant[i].second.c_str());
	throw new GeneralException(message,__FILE__,__LINE__);
      }
      else {
	//keeping the name of the antecedant
	m_input_functions[rule_number -1].push_back(function);
      }
      
    }
  }
  //cout<<"antecedant verification OK"<<endl;
  
  
  //consequent verification
  for (i = 0; i < consequent.size(); i++) {
    
    set = find_set_named(consequent[i].first, FuzzyModel::FUZZY_OUTPUT_SET);
    
    if (!set) {
      char message[256];
      sprintf(message,"SET NOT FOUND (%s)",consequent[i].first.c_str());
      throw new GeneralException(message,__FILE__,__LINE__);
    }
    else {
      function = set->find_function_by_name(consequent[i].second);
      
      
      if (!function) {
	char message[256];
	sprintf(message,"SET VARIABLE NOT FOUND (%s)(%s)",
		consequent[i].first.c_str(),consequent[i].second.c_str());
	throw new GeneralException(message,__FILE__,__LINE__);
      }
      else {
	//keeping a pointer to this function for rule evaluation
	m_output_functions[rule_number - 1].push_back(function);
      }
      
    }
  }
  //cout<<"consequent verification OK"<<endl;
  
  
  //this rule is ready to be inserted
  m_rules.push_back(ruleValue);
  
}
//////////////////////////////////////////////////////////////////////
// Add a fuzzy set to the model 
//////////////////////////////////////////////////////////////////////
void FuzzyModel::add_fuzzy_set(ObjectRef setValue, int type) {
  
  FuzzySet* set = dynamic_cast<FuzzySet*>(setValue.get());

  if (!set) {
    throw new GeneralException("NULL SET",__FILE__,__LINE__);
  }
  
  switch (type) {
  case FUZZY_INPUT_SET:
    m_input_set.push_back(setValue);
    break;
  case FUZZY_OUTPUT_SET:
    m_output_set.push_back(setValue);
    break;
  default:
    throw new GeneralException("UNKNOWN SET TYPE",__FILE__,__LINE__);
    break;
    
  }
}
//////////////////////////////////////////////////////////////////////
// Print all the rules
//////////////////////////////////////////////////////////////////////
void FuzzyModel::print_rules(ostream &out) {
  
  for (int i = 0; i < m_rules.size(); i++) {
    object_cast<FuzzyRule>(m_rules[i]).print_rule(out);
  }
  
}
//////////////////////////////////////////////////////////////////////
// verification of rule consistency
//////////////////////////////////////////////////////////////////////
void FuzzyModel::verify_rules() {
  
  
  //verification of the number of rules
  //and the antecedant/consequent part of the rules
  //to be implemented 
  int count = 1;
  
  for (int i = 0; i < m_input_set.size(); i++) {
    count *= object_cast<FuzzySet>(m_input_set[i]).get_function_count();
  }
  
  if (count != m_rules.size()) {
    char message[256];
    sprintf(message,"NUMBER OF RULES INCORRECT %i INSTEAD OF %i",m_rules.size(),count);
    throw new GeneralException(message,__FILE__,__LINE__);
  }
  
  
}
//////////////////////////////////////////////////////////////////////
// Find a set with a given name
//////////////////////////////////////////////////////////////////////
FuzzySet* FuzzyModel::find_set_named(const string &name, int type) {
  
  
  int i;
  
  switch(type) {
  case FUZZY_INPUT_SET:
    for (i =0; i < m_input_set.size(); i++) {
      if (object_cast<FuzzySet>(m_input_set[i]).get_name() == name) {
	return dynamic_cast<FuzzySet*>(m_input_set[i].get());
      }
    }    
    break;
    
  case FUZZY_OUTPUT_SET:
    
    for (i =0; i < m_output_set.size(); i++) {
      if (object_cast<FuzzySet>(m_output_set[i]).get_name() == name) {
	return dynamic_cast<FuzzySet*>(m_output_set[i].get());
      }
    }
    
    break;
    
  default:
    throw new GeneralException("UNKNOWN SET TYPE",__FILE__,__LINE__);
    break;
  }
  
  
  return NULL;
}
//////////////////////////////////////////////////////////////////////
// Evaluation/inference 
//////////////////////////////////////////////////////////////////////
Vector<float>& FuzzyModel::evaluate(Vector<float>  &input_values) {
  
  Vector<float> inputs;
  
  if (input_values.size() != m_input_set.size()) {		
    throw new GeneralException("NOT ENOUGH INPUT VARIABLES",__FILE__,__LINE__);	
  }
    
  //we must reset every output and input set
  int i;
  
  for (i = 0; i < m_input_set.size(); i++) {
    object_cast<FuzzySet>(m_input_set[i]).reset();
  }
  
  for (i =0; i < m_output_set.size(); i++) {
    object_cast<FuzzySet>(m_output_set[i]).reset();
  }
  
  //assuming that values are ordered like the rules
  for (int i = 0; i < m_input_set.size(); i++) {
    inputs.push_back(input_values[i]);
    //m_input_set[i]->get_all_membership_evaluation(input_values[i]);
  }

  //we are assuming that the input variables are in the same order than the rules
  Vector<float> conjunction_values(m_input_set.size());
  
  
  for (int x = 0; x < m_rules.size(); x++) {
    
    list<FuzzyFunction*>::iterator iter_input;
    
    int y = 0;
    
    for (iter_input = m_input_functions[x].begin();
	 iter_input != m_input_functions[x].end(); iter_input++) {

      //conjunction_values[y] = m_input_set[y]->get_value_with_name((*iter_input));
      conjunction_values[y] = (*iter_input)->evaluate(inputs[y]);

      /*
	cerr<<"input set "<<m_input_set[y]->get_name()<<endl;
	cerr<<"input name "<<(*iter_input)->get_name()<<endl;
	cerr<<"getting conjunction_value "<<conjunction_values[y]<<endl;
      */
      
      y++;

    }//for every antecedant of the rule
    
    list<FuzzyFunction*>::iterator iter;
    
    //applying conjunction operator
    for (iter = m_output_functions[x].begin();
	 iter != m_output_functions[x].end(); iter++) {
      
      /*
	cerr<<"Pushing inference value of "<<conjunction(conjunction_values)
	<<" for output function "<<(*iter)->get_name()<<endl;
      */

      (*iter)->push_inference_value(conjunction(conjunction_values));			
    }
    
  }//for every rules
  
  
  //applying disjunction (once per output function)
  float disjunction_value;
  
  
  for (i = 0; i < m_output_set.size(); i++) {
    
    Vector<ObjectRef> &funct = object_cast<FuzzySet>(m_output_set[i]).get_member_functions();
    
    for (int j = 0; j < funct.size(); j++) {
      disjunction_value = disjunction(object_cast<FuzzyFunction>(funct[j]).get_inference_values());
      object_cast<FuzzyFunction>(funct[j]).reset_inference_values();
      object_cast<FuzzyFunction>(funct[j]).push_inference_value(disjunction_value);
      /*
	printf("Final value for (%s,%s) : %f\n",m_output_set[i]->get_name().c_str(),
	funct[j]->get_name().c_str(),disjunction_value);
      */
    }
    
  }
  
  //defuzzification
  return defuzzification();
}
//////////////////////////////////////////////////////////////////////
// Printing the membership functions
//////////////////////////////////////////////////////////////////////
void FuzzyModel::print_sets(ostream &out) {
  
  out<<"INPUT SETS"<<endl;
  
  for (int i = 0; i < m_input_set.size(); i++) {
    out<<m_input_set[i]<<endl;
  }
    
  out<<"OUTPUT SETS"<<endl;
  
  for (int j = 0; j < m_output_set.size(); j++) {
    out<<m_output_set[j]<<endl;
  }
  
}


void FuzzyModel::reset() {

  //ObjectRefs
  m_rules.resize(0);
  m_output_set.resize(0);
  m_input_set.resize(0);
  
  //Function pointers
  m_input_functions.resize(0);
  m_output_functions.resize(0);

}
//////////////////////////////////////////////////////////////////////
// calculate
//////////////////////////////////////////////////////////////////////

void FuzzyModel::calculate(int output_id, int count, Buffer &out) {

  try {

    reset();
        
    //getting Fuzzy Rules
    ObjectRef Rules = getInput(m_RuleID, count);
    
    //getting Fuzzy Sets (antecedent)
    ObjectRef ASets = getInput(m_ASetID, count);
    
    //getting Fuzzy Sets (consequent)
    ObjectRef CSets = getInput(m_CSetID,count);
    
    //getting Inputs
    ObjectRef Input = getInput(m_InputID, count);
        
    //First add antecedant sets
    Vector<ObjectRef> &vect_sets1 = object_cast<Vector<ObjectRef> >(ASets);
    for (int i = 0; i < vect_sets1.size(); i++) {
      //vect_sets1[i]->printOn(cerr);
      add_fuzzy_set(vect_sets1[i]->clone(),FuzzyModel::FUZZY_INPUT_SET);
    }
    
    //Then add consequent sets
    Vector<ObjectRef> &vect_sets2 = object_cast<Vector<ObjectRef> >(CSets);
    for (int i = 0; i < vect_sets2.size(); i++) {
      //vect_sets2[i]->printOn(cerr);
      add_fuzzy_set(vect_sets2[i]->clone(),FuzzyModel::FUZZY_OUTPUT_SET);
    }
    
    //Finally add rules
    Vector<ObjectRef> &vect_rules = object_cast<Vector<ObjectRef> >(Rules);
    for (int i = 0; i < vect_rules.size(); i++) {
      //vect_rules[i]->printOn(cerr);
      add_fuzzy_rule(vect_rules[i]->clone());
    }
    
    //verify rule consistency
    verify_rules();
    
    //calculate output
    Vector<float> &vect_value = object_cast<Vector<float> >(Input);  

    Vector<float>& calc_output = evaluate(vect_value);
    
    Vector<float> *my_output = new Vector<float>(calc_output.size());
    
    for (int i = 0; i < calc_output.size(); i++) {
      (*my_output)[i] = calc_output[i];
    }
    
    if (output_id == m_OutputID) {
      out[count] = ObjectRef(my_output);
    }
    
    if (output_id == m_ModelID) {
      out[count] = clone();
    }
  }//try
  catch (BaseException *e) {
    throw e->add (new GeneralException("Exception caught while processing FuzzyModel", __FILE__, __LINE__));
  }


}
}//namespace FD


syntax highlighted by Code2HTML, v. 0.9.1