private void TopologicalSortRecursive(BayesianNode node, HashSet <BayesianNode> visited, Stack <BayesianNode> result) { if (visited.Contains(node)) { return; } // Visit the current node visited.Add(node); // Visit the children foreach (BayesianNode child in node.children) { TopologicalSortRecursive(child, visited, result); } result.Push(node); }
/// <summary> /// Given an xml string in GeNIe 2.0 format, this function parses it and creates a Bayesian Network. /// </summary> /// <param name="s">an xml string in GeNIe 2.0 format</param> /// <returns>the bayesian network parsed from the xml string</returns> public override BayesianNetwork Parse(string s) { Dictionary <string, BayesianNode> nodeMap = new Dictionary <string, BayesianNode>(); List <BayesianNode> nodes = new List <BayesianNode>(); try { XElement rootElement = XDocument.Parse(s).Root; IEnumerable <XElement> cpts = rootElement.Elements() .First(node => node.Name.LocalName.Equals("nodes")) .Elements().Where(node => node.Name.LocalName.Equals("cpt")); foreach (XElement cpt in cpts) { string name = cpt.Attribute("id").Value; string[] domains = cpt.Elements().Where(e => e.Name.LocalName.Equals("state")).Select(e => e.Attribute("id").Value).ToArray(); double[] values = ((XText)cpt.Elements().First(e => e.Name.LocalName.Equals("probabilities")).FirstNode) .Value.Split(' ').Select(v => double.Parse(v)).ToArray(); IEnumerable <XElement> parentsXmlNode = cpt.Elements().Where(e => e.Name.LocalName.Equals("parents")); BayesianNode[] parents; if (parentsXmlNode.Count() == 0) { parents = new BayesianNode[] { }; } else { parents = ((XText)parentsXmlNode.First().FirstNode).Value.Split(' ').Select(n => nodeMap[n]).ToArray(); } BayesianNode node = new BayesianNode(name, domains, values, parents); nodes.Add(node); nodeMap.Add(name, node); } return(new BayesianNetwork(nodes.ToArray())); } catch (Exception e) { throw new FormatException("Encounter error when trying to read in the network from the GeNie file. " + "If you believe it is not your fault, please report to the author of this package." + "(Reason: " + e.Message + " " + e.StackTrace + ")" ); } }
/// <summary> /// Given a JSON string, this function parses it and creates a Bayesian Network. /// </summary> /// <param name="s">JSON string</param> /// <returns>a bayesian network parsed from the json string</returns> public override BayesianNetwork Parse(string json) { Dictionary <string, BayesianNode> nodeMap = new Dictionary <string, BayesianNode>(); List <BayesianNode> nodes = new List <BayesianNode>(); Dictionary <string, object> jsonObj; try { jsonObj = Json.Deserialize(json) as Dictionary <string, object>; } catch (Exception e) { throw new FormatException("The string is not valid JSON. Error detail: " + e.Message + " " + e.StackTrace); } try { List <object> jsonNodes = (List <object>)jsonObj["nodes"]; foreach (Dictionary <string, object> jsonNode in jsonNodes) { string name = (string)jsonNode["name"]; string[] domains = ((List <object>)jsonNode["domain"]).Select(d => (string)d).ToArray(); double[] values = ((List <object>)jsonNode["values"]).Select(v => (double)v).ToArray(); BayesianNode[] parents = ((List <object>)jsonNode["parents"]).Select(n => nodeMap[(string)n]).ToArray(); BayesianNode node = new BayesianNode(name, domains, values, parents); nodes.Add(node); nodeMap.Add(name, node); } return(new BayesianNetwork(nodes.ToArray())); } catch (Exception e) { throw new FormatException("Encounter error when trying to read in the network from the file." + "Most likely the JSON file is not valid or the nodes are not valid. " + "If you believe it is not your fault, please report to the author of this package." + "Actual Error (Reason: " + e.Message + " " + e.StackTrace + ")" ); } }
private bool MarkVariablesRecursive(BayesianNode node, HashSet <BayesianNode> relevantNodes, HashSet <BayesianNode> irrelevantNodes) { if (relevantNodes.Contains(node)) { return(true); } if (irrelevantNodes.Contains(node)) { return(false); } foreach (BayesianNode child in node.children) { if (MarkVariablesRecursive(child, relevantNodes, irrelevantNodes)) { relevantNodes.Add(node); } } return(relevantNodes.Contains(node)); }
/// <summary> /// Given a BayesianNode object and an array of Proposition objects as observations, /// this function perform inferences and return the inferred distribution of the node variable. /// </summary> /// <param name="query">the BayesianNode object being query</param> /// <param name="observations">the array of Proposition objects</param> /// <returns>a distribution table for the values of the query node</returns> public double[] Infer(BayesianNode query, params Proposition[] observations) { try { string[] evidenceNames = observations.Select(o => o.name).ToArray(); IEnumerable <BayesianNode> nodes = TopologicalSort(network.GetNodes()).Reverse(); HashSet <BayesianNode> relevantNodes = new HashSet <BayesianNode>(); relevantNodes.Add(query); foreach (Proposition p in observations) { relevantNodes.Add(network.FindNode(p.name)); } MarkRelevantVariables(relevantNodes, nodes); // Remove variables that are not ancestor of a query variable or evidence variable nodes = nodes.Intersect(relevantNodes); List <CPT> factors = new List <CPT>(); foreach (BayesianNode node in nodes) { factors.Add(node.MakeFactor(observations)); if (node.var.name.Equals(query.var.name) || Array.IndexOf(evidenceNames, node.var.name) >= 0) { continue; } CPT[] factorsToSumOut = factors.Where(f => f.ContainsVar(node.var)).ToArray(); factors.RemoveAll(f => Array.IndexOf(factorsToSumOut, f) != -1); CPT factorToSumOut; if (factorsToSumOut.Count() > 1) { factorToSumOut = factorsToSumOut.Skip(1).Aggregate(factorsToSumOut.First(), (acc, f) => acc.PointWiseProduct(f)); } else { factorToSumOut = factorsToSumOut.First(); } CPT afterSumOut = factorToSumOut.SumOut(node.var); factors.Add(afterSumOut); } CPT result; if (factors.Count() > 1) { result = factors.Skip(1).Aggregate <CPT, CPT>(factors.First(), (acc, f) => acc.PointWiseProduct(f)); } else { result = factors.First(); } return(result.Distribution()); } catch (Exception e) { throw new Exception("Unable to perform inference on the network. " + "Please make sure the network is valid and the propositions are valid. " + "Contact the author of this library if you suspect it is a problem in the library." + "Actual Error (Reason: " + e.Message + " " + e.StackTrace + ")" ); } }
/// <summary> /// Given a BayesianNode object and a list of Proposition objects as observations, /// this function perform inferences and return the inferred distribution of the node variable. /// </summary> /// <param name="query">the BayesianNode object being query</param> /// <param name="observations">the list of Proposition objects</param> /// <returns>a distribution table for the values of the query node</returns> public double[] Infer(BayesianNode query, IEnumerable <Proposition> observations) { return(Infer(query, observations.ToArray())); }
/// <summary> /// Get the array of all the nodes in the network. The array is a copy of the internal /// structure but the BayesianNode objects are not clones. It means a modification on the node /// results in a modification of the same node in the network. /// </summary> /// <returns>an array of all the nodes in the network</returns> public BayesianNode[] GetNodes() { BayesianNode[] result = new BayesianNode[nodes.Length]; Array.Copy(nodes, result, nodes.Length); return(result); }