package org.societies.context.user.refinement.impl.bayesianLibrary.inference.structures.impl.bayeslets;
import java.util.ArrayList;
import java.util.Arrays;
import org.societies.context.user.refinement.impl.bayesianLibrary.inference.solving.utils.Util;
import org.societies.context.user.refinement.impl.bayesianLibrary.inference.structures.impl.Node;
import org.societies.context.user.refinement.impl.bayesianLibrary.inference.structures.impl.Probability;
import org.societies.context.user.refinement.impl.bayesianLibrary.inference.structures.impl.ProbabilityDistribution;
import org.societies.context.user.refinement.impl.bayesianLibrary.inference.structures.interfaces.HasProbabilityTable;
/**
* @author gall_pa, fran_ko, fort_se
*/
public class ProbabilityDistributionBL extends ProbabilityDistribution{
private HasProbabilityTable owner;
private Probability[] allCases;
/**
* @param node
*/
public ProbabilityDistributionBL(HasProbabilityTable node) {
super();
setOwner(node);
}
public ProbabilityDistributionBL(HasProbabilityTable owner2,
Probability[] newProbs) {
super(owner2, newProbs);
}
/**
* This method combines the probabilities tables from two nodes. It is used when interface nodes are combined.
*/
public void combineProbabilityDistribution(Node sourceNode, Node targetNode){
Probability[] temp=getAllCases();
Probability[] inputProbabilities=sourceNode.getProbTable().getProbabilities();
int expandConstant=Util.getNumberStates(Util.getParents(sourceNode.getIncoming()));
int initialNumberCases=Util.getNumberStates(Util.getParents(targetNode.getIncoming()));
if (initialNumberCases==0){
//In this case nodeTarget has no parents. The probabilities from nodeSource will be copied
//to nodeTarget.
this.setAllCases(inputProbabilities);
}else if((initialNumberCases!=0)&&(expandConstant!=0)){
// both targetNode and sourceNode have parents
int cont=0;
int newProbabilityLength=(temp.length)*expandConstant;
int index=0;
int writingIndex=0;
int inputIndex=0;
int inputIndexStartingPosition=0;
//this.expandProbTable(expandConstant);
setAllCases(new Probability[newProbabilityLength]);
/* The next while structure provides with an extension of the original probability distribution
* conformed by a number of copies of it determined by expandConstant.
*/
while (index<temp.length){
for (int j=0;j<expandConstant;j++){
getAllCases()[writingIndex+j]=temp[index];
}
index++;
writingIndex=writingIndex+expandConstant;
}
index=0;
/* The next while structure updates the probability distribution with the states and the probabilities
* provided by the probability distribution to be combined.
*/
while(index<newProbabilityLength){
double tempProbability=getAllCases()[index].getProbability();
double tempInputProbability=inputProbabilities[inputIndex].getProbability();
String[] tempInputStates=inputProbabilities[inputIndex].getStates();
String[] tempStates=getAllCases()[index].getStates();
String[] newStates=new String[tempStates.length+tempInputStates.length-1];
for (int i=0;i<tempStates.length;i++){ //Original states
newStates[i]=tempStates[i];
}
for (int i=0;i<tempInputStates.length-1;i++){ //Adding new states
newStates[i+tempStates.length]=tempInputStates[1+i];
}
tempProbability=(tempProbability)*(tempInputProbability);
getAllCases()[index]=new Probability(newStates,tempProbability);
inputIndex++;
index++;
if ((inputIndex%expandConstant)==0){ // Updating reading and writing indexes
cont++;
if (cont==initialNumberCases){
inputIndexStartingPosition=inputIndexStartingPosition+expandConstant;
cont=0;
}
inputIndex=inputIndexStartingPosition;
}
}
//System.out.println("Before normalizing");
//System.out.println(this.toString());
this.normalize();
}
}
/**
* This method combines two probabilities tables. It is used when combining nodes with common parents. The tables must have been previously reordered, so
* the common parents are just after the owner node from the respective probabilities table.
* @param input Node whose probabilities table we want to combine with this one.
* @param expand Number of combinations of the incoming edges of node input that are related to parents different from the common ones.
* @param position Position where the non-common parents start in the probabilities table.
*/
public void combineTable (Node input, int expand, int position){
int indexTable=0;
int indexInput=0;
int cont=0;
int casesR=((NodeBL)owner).getRightCases(0);
int startingPositionInput=0;
Probability[] inputP=input.getProbTable().getProbabilities();
while (indexTable<this.getAllCases().length){
String[] tempStates=inputP[indexInput+startingPositionInput].getStates();
double tempInputProb = inputP[indexInput+startingPositionInput].getProbability();
ArrayList<String[]> tempStatesArrayList=Util.arrayToArrayList(tempStates);
for (int j=0;j<=position;j++){
tempStatesArrayList.remove(0);
}
ArrayList<String[]> oldStates=Util.arrayToArrayList(getAllCases()[indexTable].getStates());
oldStates.addAll(tempStatesArrayList);
getAllCases()[indexTable].setStates(Util.convertToArrayOfStrings(oldStates));
getAllCases()[indexTable].setProbability(getAllCases()[indexTable].getProbability()*tempInputProb);
indexInput++;
if (indexInput==expand){
cont++;
indexInput=0;
if (cont==casesR){
startingPositionInput=startingPositionInput+expand;
cont=0;
}
}
indexTable++;
}
this.normalize();
}
/**
* Normalizes the probabilities table, so the sum of probabilities of every possible state from the
* owner node is one.
*
*/
public void normalize(){
double sum=0;
int jumpIndex=0;
int statesNumber=((Node)owner).countStates();
int index=0;
while(index<(getAllCases().length/statesNumber)){
sum=0;
for (jumpIndex=0;jumpIndex<statesNumber;jumpIndex++){
int pos=index+(jumpIndex*(getAllCases().length/statesNumber));
sum=sum+getAllCases()[pos].getProbability();
}
for (jumpIndex=0;jumpIndex<statesNumber;jumpIndex++){
int pos2=index+(jumpIndex*(getAllCases().length/statesNumber));
double tempProbability=getAllCases()[pos2].getProbability();
tempProbability=(tempProbability)/sum;
getAllCases()[pos2]=new Probability(getAllCases()[index+(jumpIndex*(getAllCases().length/statesNumber))].getStates(),tempProbability);
}
index++;
}
}
public String toStringWithoutParents()
{
//String back = "Marginalization of Node " + owner.getName() +":\n\n";
//back += "\t\t\tProbability\n\n";
String back="";
for (int i=0; i<getAllCases().length;i++){
Probability pb = (Probability)getAllCases()[i];
back += pb + "\n";
}
return back;
}
/**
* This method provides with an extension of the original probability distribution
* conformed by a number of copies of it determined by expandConstant.
* @param expandConstant
*/
public void expandProbTable(int expandConstant){
Probability[] temp=getAllCases();
int newProbabilityLength=(this.getProbabilities().length)*expandConstant;
setAllCases(new Probability[newProbabilityLength]);
int index=0;
int writingIndex=0;
while (index<temp.length){
for (int j=0;j<expandConstant;j++){
getAllCases()[writingIndex+j]=(Probability) temp[index].clone();
}
index++;
writingIndex=writingIndex+expandConstant;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////Added by Sergio//////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
public Object clone(){
Probability[] newProbs = new Probability[this.getAllCases().length];
for (int i=0; i<this.getAllCases().length; i++){
newProbs[i]=(Probability) this.getAllCases()[i].clone();
}
ProbabilityDistributionBL neu = new ProbabilityDistributionBL(this.owner, newProbs);
return neu;
}
/**
* This methods return the Probability of the most probability state.
* It is useful for likelihood, marginalization, accumulative inference,
* ...
* @return
*/
public Probability getMostProbable(){
Probability mostProbable= this.getProbabilities()[0];
for (int k=0; k<this.getProbabilities().length; k++){
if (this.getProbabilities()[k].getProbability()>mostProbable.getProbability()){
mostProbable=this.getProbabilities()[k];
}
}
return mostProbable;
}
/**
* A method to find all indices in this object's probability table that match with the value-configuration passed as parameters
* !!Only ok if the order have been saved.
* @param participants the family of the node, whose table are to be matched
* @param states: 1 Combination of values of a node's potential that is to be found in the clique's (clique = this) Potential
* @return
*/
public int[] fitsIndexJointTables(Node[] participants, String[] states) {
Node[] representedInClique = getOwner().getParticipants();
ArrayList<Node> riC_list = new ArrayList<Node>(Arrays.asList(representedInClique));
ArrayList<Node> nodepart_list = new ArrayList<Node>(Arrays.asList(participants));
if (!riC_list.containsAll(nodepart_list)){
System.err.println("ProbabilityDistribution: fitsIndex: Node-family NOT CONTAINED IN CLIQUE!");
return null;
}
//Calculate Positions
ArrayList<Integer> results = new ArrayList<Integer>();
int[] positions = new int[participants.length];
//The positions must be the same that the order of the provided nodes
if (riC_list.size()>=participants.length){
for (int i=0;i<positions.length;i++) positions[i]=i;
}
else {
System.err.println("> fitsIndexJointTable : State configuration so much longer for this probTable");
}
/*
* 'Brute' method - indices are searched, not calculated.
* number of state * 'Wertigkeit' der stelle [= wieviele werte die hinteren variablen haben]
*/
for (int i=0;i<getAllCases().length;i++)
{
int equals = 0;
String[] configuration = getAllCases()[i].getStates();
for (int j=0;j<states.length;j++){
if (!states[j].equals(configuration[positions[j]])) break;
else equals++;
}
if (equals==states.length) results.add(new Integer(i));//[resultindex++]=i;
}
int[] resultarray = new int[results.size()];
for (int i=0;i<resultarray.length;i++) resultarray[i]=((Integer)results.get(i)).intValue();
return resultarray;
}
public void setOwner(HasProbabilityTable owner) {
this.owner = owner;
}
public HasProbabilityTable getOwner() {
return owner;
}
public void setAllCases(Probability[] allCases) {
this.allCases = allCases;
}
public Probability[] getAllCases() {
return allCases;
}
}