/*
* Encog(tm) Core v3.3 - Java Version
* http://www.heatonresearch.com/encog/
* https://github.com/encog/encog-java-core
* Copyright 2008-2014 Heaton Research, Inc.
*
* 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.
*
* For more information on Heaton Research copyrights, licenses
* and trademarks visit:
* http://www.heatonresearch.com/copyright
*/
package org.encog.ml.bayesian.bif;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.encog.Encog;
import org.encog.ml.bayesian.BayesianChoice;
import org.encog.ml.bayesian.BayesianError;
import org.encog.ml.bayesian.BayesianEvent;
import org.encog.ml.bayesian.BayesianNetwork;
import org.encog.ml.bayesian.table.TableLine;
import org.encog.parse.tags.write.WriteXML;
import org.encog.util.csv.CSVFormat;
import org.xml.sax.SAXException;
/**
* A utility class to read and write Bayesian networks in BIF format.
*
* http://www.heatonresearch.com/wiki/Bayesian_Interchange_Format
*/
public class BIFUtil {
/**
* Read a BIF file.
*
* @param f
* The BIF file.
* @return The Bayesian network that was read.
*/
public static BayesianNetwork readBIF(String f) {
return readBIF(new File(f));
}
public static BayesianNetwork readBIF(File f) {
FileInputStream fis = null;
try {
fis = new FileInputStream(f);
return readBIF(fis);
} catch (IOException ex) {
throw new BayesianError(ex);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException ex) {
// who cares at this point.
}
}
}
}
/**
* Read a BIF file from a stream.
*
* @param is
* The stream to read from.
* @return The Bayesian network read.
*/
public static BayesianNetwork readBIF(InputStream is) {
try {
BIFHandler h = new BIFHandler();
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
sp.parse(is, h);
return h.getNetwork();
} catch (IOException ex) {
throw new BayesianError(ex);
} catch (ParserConfigurationException ex) {
throw new BayesianError(ex);
} catch (SAXException ex) {
throw new BayesianError(ex);
}
}
/**
* Write a Bayesian network to BIF form.
*
* @param fn
* The file name to save to.
* @param network
* The network to save.
*/
public static void writeBIF(String fn, BayesianNetwork network) {
writeBIF(new File(fn), network);
}
/**
* Write a Bayesian network to a BIF file.
*
* @param file
* The file to save to.
* @param network
* The network to save.
*/
public static void writeBIF(File file, BayesianNetwork network) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
writeBIF(fos, network);
} catch (IOException ex) {
throw new BayesianError(ex);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// don't care at this point
}
}
}
}
/**
* Write a Bayesian network to an output stream in BIF format.
*
* @param os
* The output stream to write to.
* @param network
* The network to write.
*/
public static void writeBIF(OutputStream os, BayesianNetwork network) {
WriteXML xml = new WriteXML(os);
xml.beginDocument();
xml.addAttribute("VERSION", "0.3");
xml.beginTag("BIF");
xml.beginTag("NETWORK");
xml.addProperty("NAME", "Bayes Network, Generated by Encog");
// write variables
for (BayesianEvent event : network.getEvents()) {
xml.addAttribute("TYPE", "nature");
xml.beginTag("VARIABLE");
xml.addProperty("NAME", event.getLabel());
for (BayesianChoice str : event.getChoices()) {
xml.addProperty("OUTCOME", str.getLabel());
}
xml.endTag();
}
// write relations
for (BayesianEvent event : network.getEvents()) {
xml.beginTag("DEFINITION");
xml.addProperty("FOR", event.getLabel());
for (BayesianEvent parentEvent : event.getParents()) {
xml.addProperty("GIVEN", parentEvent.getLabel());
}
xml.addAttribute("TABLE", generateTable(event));
xml.endTag();
}
xml.endTag();
xml.endTag();
xml.endDocument();
}
/**
* Generate a table, in BIF format.
*
* @param event
* The event to write.
* @return The string form of the table.
*/
public static String generateTable(BayesianEvent event) {
StringBuilder s = new StringBuilder();
int tableIndex = 0;
int[] args = new int[event.getParents().size()];
do {
for (int result = 0; result < event.getChoices().size(); result++) {
TableLine line = event.getTable().findLine(result, args);
if (s.length() > 0) {
s.append(" ");
}
s.append(CSVFormat.EG_FORMAT.format(line.getProbability(),
Encog.DEFAULT_PRECISION));
}
} while (BIFUtil.rollArgs(event, args));
return s.toString();
}
/**
* Iterate through the event arguments in the BIF way, which is different
* than Encog's method.
*
* @param event
* The event to save.
* @param args
* The arguments.
* @return True if there is further to iterate.
*/
public static boolean rollArgs(BayesianEvent event, int[] args) {
int currentIndex = event.getParents().size() - 1;
boolean done = false;
boolean eof = false;
if (event.getParents().size() == 0) {
done = true;
eof = true;
}
while (!done) {
int v = (int) args[currentIndex];
v++;
if (v >= event.getParents().get(currentIndex).getChoices().size()) {
args[currentIndex] = 0;
} else {
args[currentIndex] = v;
done = true;
break;
}
currentIndex--;
if (currentIndex < 0) {
done = true;
eof = true;
}
}
return !eof;
}
}