/*
        Create a simple Bayesian Network and use structure learning.
        Alvaro Marin - split@splitcc.net
        Abril 2005.     
*/

#include "stdio.h"
#include "pnl_dll.hpp"
#include "cvsvd.h"
#include "pnlMlStaticStructLearn.hpp"
#include "pnlMlStaticStructLearnHC.hpp"

PNL_USING

void printBayesianNetwork( CBNet *netbay )
{
                
        const CFactor *pCPD;
        const CNumericDenseMatrix *pMatForCPD;
        const float *dataCPD;
        int numOfEl; 
        int f; 

        // Get information from learned model (number of factors)
        int nFactors = netbay->GetNumberOfFactors();
  
        for( f = 0; f < nFactors; f++ )
        {
                std::cout<GetFactor(f);
                pMatForCPD = static_cast *>(pCPD->GetMatrix(matTable));
                pMatForCPD->GetRawData( &numOfEl, &dataCPD );
                int j;
                for( j = 0; j < numOfEl; j++ )
                {
                        std::cout<<" "<AddNodes(numOfNds);
	pGraph->AddEdge(0,1,1);
	pGraph->AddEdge(0,2,1);
	pGraph->AddEdge(1,3,1);
	pGraph->AddEdge(2,3,1);
	
	pGraph->Dump();

	CNodeType *nodeTypes = new CNodeType [4];
	nodeTypes[0].SetType(1, 2);
	nodeTypes[1].SetType(1, 2);
	nodeTypes[2].SetType(1, 2);
	nodeTypes[3].SetType(1, 2);
	
	int *nodeAssociation = new int[numOfNds];
	for ( int i = 0; i < numOfNds; i++ )
    	{
        	nodeAssociation[i] = i;
    	}

	CBNet *pBNet;
	try{
		pBNet = CBNet::Create( numOfNds, numOfNds /*NumOfNodeTypes*/, nodeTypes, nodeAssociation, pGraph );
	}
	catch (pnl::CException ex)
  	{
		printf("[PNL] EXCEPTION %d: %s\n",ex.GetCode(),ex.GetMessage());
  	}	

	float table0[] = { 0.5f, 0.5f }; 
	float table1[] = { 0.5f, 0.5f }; 
	float table2[] = { 0.5f, 0.5f }; 
	float table3[] = { 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f };
	
	float* table[] = { table0, table1, table2, table3 }; 
		
	// Factor == CPD == Celda. Vamos alojando los CPD (Conditional Probabilistic Distribution) en cada Factor
	for( int i = 0; i < numOfNds; ++i ) 
	{ 
		pBNet->AllocFactor(i); 
		CFactor* pFactor = pBNet->GetFactor(i); 
		pFactor->AllocMatrix( table[i], matTable );			
	}

	printf("\n[PNL] pBNet created.\n");
	printBayesianNetwork(pBNet);
	
	getchar();

	return pBNet;
}




void structureLearning(CBNet *pBNetAux){

	const CFactor *pCPD;
	const CNumericDenseMatrix *pMatForCPD;
	const float *dataCPD2;
	int numOfEl; 
  	int f,i; 
	int nFactors;
	
	// Structure Learning
	CMlStaticStructLearnHC *pLearnS;	
	CModelDomain *pMD;
		
	/* STRUCTURE LEARNING ENGINE */
		
	printf("[PNL] Starting Structure Learning...\n");
		
	// Create learning engine
	/* Hill-climbing algorithm */
	intVector vA;
	intVector vD;
		
	try
	{
     		pLearnS = CMlStaticStructLearnHC::Create(pBNetAux, itStructLearnML, StructLearnHC, BIC, 4, vA, vD, 1);
	}		
    	catch (pnl::CException ex)
    	{
		printf("[PNL] EXCEPTION %d: %s\n",ex.GetCode(),ex.GetMessage());
    	}		

	// Create the evidences vector
	pEvidencesVector evVec;		

	pMD = pBNetAux->GetModelDomain();

	// Loading data from file
  	const char * fname = "bayesianstructure.data";
	try{
  		if( ! CEvidence::Load(fname,  &evVec, pMD) )
  		{
	  		printf("[PNL] Can't open file with cases.\n");
		}
		else 
			printf("[PNL] Structure archive loaded ok.\n");
		}		
    	catch (pnl::CException ex)
    	{
		printf("[PNL] EXCEPTION %d: %s\n",ex.GetCode(),ex.GetMessage());
    	}	
		
		
	int numOfSamples = evVec.size();
  	std::cout<<"[PNL] Number of cases for learning the structure = "<SetData(numOfSamples, &evVec.front());
		//start learning
		pLearnS->Learn();
	}		
    	catch (pnl::CException ex)
    	{
		printf("[PNL] EXCEPTION %d: %s\n",ex.GetCode(),ex.GetMessage());
    	}	


	// The output graphical model is sorted topologically
	// Get the relation to initial node numeration
	//const int *reordering = pLearn->GetOrder();
		
	printBayesianNetwork(pBNetAux);
	
	// Hill-climbing
	const CDAG* pDAG = pLearnS->GetResultDAG();
	//    CDAG* iDAG = CDAG::Create(*(netbay->GetGraph()));
	//    int diff = iDAG->SymmetricDifference(pDAG);
	 pDAG->Dump();

		
	try{
		// Create the BNet to fetch it with GetResultBNet
		pLearnS->CreateResultBNet(const_cast(pLearnS->GetResultDAG()));
		pBNetAux = const_cast(pLearnS->GetResultBNet());	
	}		
    	catch (pnl::CException ex)
    	{
		printf("[PNL] EXCEPTION %d: %s\n",ex.GetCode(),ex.GetMessage());
    	}		

	if (pBNetAux == NULL )
		printf("[PNL] ERROR. NULL pointers to Bayesian Networks.\n");
		
	int ev;
  	for( ev = 0; ev < evVec.size(); ev++ )
  	{
		delete evVec[ev];
	}
		
	printf("[PNL] Structure Learning completed.\n");
		
}



int main(int argc, char* argv[])
{
	
	printf("\n\nProceso de aprendizaje de estructura.\n\nCrearemos la siguiente red bayesiana:\n\n");
	printf("    0\n   / \\\n  1   2\n   \\ / \n    3 \n\n");
	getchar();
	CBNet *pBNetAux=createBNet();
	structureLearning(pBNetAux);
	return 0;

}