/******************************************************************************* * Copyright (c) 2011 Dipanjan Das * Language Technologies Institute, * Carnegie Mellon University, * All Rights Reserved. * * WeightedRootedHyperDAG.java is part of SEMAFOR 2.0. * * SEMAFOR 2.0 is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SEMAFOR 2.0 is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with SEMAFOR 2.0. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package edu.cmu.cs.lti.ark.util.ds.graph; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import edu.cmu.cs.lti.ark.util.ds.map.CounterMap; import edu.cmu.cs.lti.ark.util.ds.map.FactoryDefaultMap; import edu.cmu.cs.lti.ark.util.ds.map.FactoryDefaultMap.DefaultValueFactory; import edu.cmu.cs.lti.ark.util.ds.path.IndexedLabeledPath; import edu.cmu.cs.lti.ark.util.ds.path.IndexedPath; import edu.cmu.cs.lti.ark.util.ds.path.LabeledPath; import edu.cmu.cs.lti.ark.util.ds.path.Path; import edu.cmu.cs.lti.ark.util.ds.ArrayUtil; import edu.cmu.cs.lti.ark.util.ds.Pair; import edu.cmu.cs.lti.ark.util.optimization.LDouble; import edu.cmu.cs.lti.ark.util.optimization.LogFormula; import edu.cmu.cs.lti.ark.util.optimization.LDouble.IdentityElement; import edu.cmu.cs.lti.ark.util.optimization.LogFormula.Op; import edu.cmu.cs.lti.ark.util.Indexer; import edu.cmu.cs.lti.ark.util.Semirings; import edu.cmu.cs.lti.ark.util.Subroutine; import gnu.trove.THashSet; /** * DAG class with a general-purpose implementation of Dijkstra's algorithm * wherein multiple edges in a path may be considered at once in scoring * (i.e. allowing for an arbitrarily large Markov order). * <br/><br/> * Note: other type parameters used in methods of this class include: * <dl><dt>{@code <L>}</dt><dd>Type of the label associated with each edge, if any</dd> * <dt>{@code <V>}</dt><dd>Semiring value associated with a path</dd> * <dt>{@code <I>}</dt><dd>Indexer, i.e. a path instance where nodes are indicated with integers</dd> * <dt>{@code <T>},{@code <U>}</dt><dd>Type of {@link Path} elements: if the path is labeled, this will * be a (node, label) pair, i.e. {@link LabeledPath}{@code<N,L> extends }{@link Path}{@code<Pair<N,L>>}; * otherwise it will be a node, i.e. {@link Path}{@code<N>}</dd></dl> * * @author Nathan Schneider (nschneid) * * @param <N> Graph node type * @param <W> Edge weight type */ public class WeightedRootedHyperDAG<N extends RootedDAGNode<N> & HasWeightedHyperEdges<N,W>,W> extends RootedDAG<N> { /** @deprecated */ protected CounterMap<N,N> _weights; public void initLogger(FileWriter f) { _logger = new DebugLogger(f); } protected class DebugLogger { FileWriter _f; public DebugLogger(FileWriter f) { _f = f; } public void log(int[] ii, int[] jj, int chartSize) { writeString(String.format("%s\t%s\t%d\n", Arrays.toString(ii), Arrays.toString(jj), chartSize)); } public void close() { try { _f.close(); } catch (IOException e) { e.printStackTrace(); } } public void popped(Object poppedItem, int newAgendaSize) { writeString(String.format("%s\t%d\n", poppedItem.toString(), newAgendaSize)); } public void height(int nodeHeight) { writeString(String.format("%d", nodeHeight)); } protected void writeString(String s) { try { _f.write(s); } catch (IOException e) { e.printStackTrace(); } } } protected DebugLogger _logger; /** @deprecated For testing purposes only; normally edge weights should be accessible via the source node */ public WeightedRootedHyperDAG(N root, CounterMap<N,N> weights) { super(root); _weights = weights; } public WeightedRootedHyperDAG(N root) { super(root); } /** * Simple version: * arrive(j) (+)= start(j) * arrive(j) (+)= arrive(H) (*) arc(H, j) * goal(j) (+)= arrive(j) (*) end(j) * * @param semr * @return * @throws GraphException */ public <V> Object computePath(Semirings.Semiring<V> semr) throws GraphException { return computeUnlabeledHyperpath(semr,1); } public <V> V computeUnlabeledHyperpath(Semirings.Semiring<V> semr, int order) throws GraphException { return computeUnlabeledHyperpath(semr,order,null); } public <V> V computeUnlabeledHyperpath(int order, Subroutine edgeOperation) throws GraphException { return (V)computeULHyperpath(null,order,null,edgeOperation); } public <V> V computeUnlabeledHyperpath(Semirings.Semiring<V> semr, int order, Path<N> relevantPath) throws GraphException { return computeHyperpath(semr,order,relevantPath,null); } public <V> V computeLabeledHyperpath(Semirings.Semiring<V> semr, int order) throws GraphException { return computeLabeledHyperpath(semr,order,null); } public <V,L> V computeLabeledHyperpath(Semirings.Semiring<V> semr, int order, LabeledPath<N,L> relevantPath) throws GraphException { return computeHyperpath(semr,order,relevantPath,null); } public <V> V computeLabeledHyperpath(int order, Subroutine edgeOperation) throws GraphException { return (V)computeLHyperpath(null,order,null,edgeOperation); } /** Initialize the chart and agenda for decoding in the unlabeled case. */ public <V> Pair<Map<IndexedPath<N>,V>, SortedSet<IndexedPath<N>>> initUnlabeledChartAndAgenda(Semirings.Semiring<V> semr, int order) { Map<IndexedPath<N>,V> chart = null; if (semr!=null) chart = new FactoryDefaultMap<IndexedPath<N>,V>(new SemiringZeroFactory<V>(semr)); int[] ii = new int[order+1]; for (int k=0; k<order; k++) { ii[k] = 0; // START symbol } ii[order] = 1; // index of head node of the path IndexedPath<N> initialItem = new IndexedPath<N>(ArrayUtil.toArrayList(ii),true); // initialize chart if (chart!=null) chart.put(initialItem, semr.oone()); SortedSet<IndexedPath<N>> agenda = new TreeSet<IndexedPath<N>>(new Comparator<IndexedPath<N>>() { @Override public int compare(IndexedPath<N> o1, IndexedPath<N> o2) { // to determine priority, sort by the last element of the int[], // i.e. the index of the node to arrive at; ensures nodes are arrived at from left to right, starting with the root // For comparing lists of the form [a, b, c], where c is the arrival node, primary sort order is ascending by c, secondary sort order // is DESCENDING by b, ternary sort order is descending by a, etc. assert o1.size()==o2.size(); boolean latest = true; for (int i=o1.size()-1; i>=0; i--) { if (latest) { if (o1.get(i)<o2.get(i)) return -1; if (o1.get(i)>o2.get(i)) return 1; } else { if (o1.get(i)<o2.get(i)) return 1; if (o1.get(i)>o2.get(i)) return -1; } } return 0; } }); agenda.add(initialItem); return new Pair<Map<IndexedPath<N>,V>, SortedSet<IndexedPath<N>>>(chart, agenda); } /** Initialize the chart and agenda for decoding in the unlabeled case. */ public <V,L> Pair<Map<IndexedLabeledPath<N,L>,V>, SortedSet<IndexedLabeledPath<N,L>>> initLabeledChartAndAgenda(Semirings.Semiring<V> semr, int order, L dummy) { Map<IndexedLabeledPath<N,L>,V> chart = null; if (semr!=null) chart = new FactoryDefaultMap<IndexedLabeledPath<N,L>,V>(new SemiringZeroFactory<V>(semr)); int[] ii = new int[order+1]; List<L> lbls = new ArrayList<L>(); for (int k=0; k<order; k++) { ii[k] = 0; // START symbol lbls.add(null); } ii[order] = 1; // index of head node of the path IndexedLabeledPath<N,L> initialItem = new IndexedLabeledPath<N,L>(ii,true,lbls,true); // initialize chart if (chart!=null) chart.put(initialItem, semr.oone()); SortedSet<IndexedLabeledPath<N,L>> agenda = new TreeSet<IndexedLabeledPath<N,L>>(); agenda.add(initialItem); return new Pair<Map<IndexedLabeledPath<N,L>,V>, SortedSet<IndexedLabeledPath<N,L>>>(chart, agenda); } public <V,L> V computeHyperpath(Semirings.Semiring<V> semr, int order, Path<N> path, Subroutine edgeOperation) throws GraphException { return computeULHyperpath(semr,order,path,edgeOperation); } public <V,L> V computeULHyperpath(Semirings.Semiring<V> semr, int order, Path<N> path, Subroutine edgeOperation) throws GraphException { N r = (N)_root; List<N> sortedNodes = RootedDAGNode.topologicalSort(r); sortedNodes.add(0,null); // a START symbol if (path!=null && path.isSourceIncluded() && path.getSource()!=sortedNodes.get(1)) throw new GraphException("path must start with the root node of the graph"); return _startAgenda(false, this.initUnlabeledChartAndAgenda(semr, order), sortedNodes, semr, order, path, edgeOperation); } public <V,L> V computeHyperpath(Semirings.Semiring<V> semr, int order, LabeledPath<N,L> path, Subroutine edgeOperation) throws GraphException { return computeLHyperpath(semr,order,path,edgeOperation); } public <V,L> V computeLHyperpath(Semirings.Semiring<V> semr, int order, LabeledPath<N,L> path, Subroutine edgeOperation) throws GraphException { N r = (N)_root; List<N> sortedNodes = RootedDAGNode.topologicalSort(r); sortedNodes.add(0,null); // a START symbol if (path!=null && path.isSourceIncluded() && path.getSource()!=sortedNodes.get(1)) throw new GraphException("path must start with the root node of the graph"); return _startAgenda(true, this.initLabeledChartAndAgenda(semr, order, (path==null) ? null : path.getLabels().get(0)), sortedNodes, semr, order, path, edgeOperation); } protected <T,U,V,I extends Path<T> & Indexer<List<Integer>,List<N>,? extends Path<U>>,L> V _startAgenda(boolean isLabeled, Pair<Map<I,V>, SortedSet<I>> chartAndAgenda, List<N> sortedNodes, Semirings.Semiring<V> semr, int order, Path<U> relevantPath, Subroutine edgeOperation) { Map<I,V> chart = chartAndAgenda.getFirst(); SortedSet<I> agenda = chartAndAgenda.getSecond(); while (!agenda.isEmpty()) { I nextItem = agenda.first(); agenda.remove(nextItem); // pop if (_logger!=null) _logger.popped(nextItem, agenda.size()); for (I item : arrive(isLabeled, sortedNodes, chart, semr, nextItem, relevantPath, edgeOperation)) { if (!agenda.contains(item)) { agenda.add(item); // arrive at node jj[-1] given history jj[:-1] } } if (relevantPath!=null) relevantPath = relevantPath.getTail(); } //if (_logger!=null && relevantNodes==null) // _logger.close(); if (semr!=null) // sum of all histories going into the last node return arrivalsTo(sortedNodes, chart, semr, sortedNodes.size()-1, order); return null; } public static class SemiringZeroFactory<V> implements DefaultValueFactory<V> { private Semirings.Semiring<V> _semiring; public <W> SemiringZeroFactory(Semirings.Semiring<V> semiring) { _semiring = semiring; } public V newDefaultValue() { return _semiring.ozero(); } } /** chart lookup */ protected static <V,T,U,N,I extends Path<T> & Indexer<List<Integer>,List<N>,? extends Path<U>>> V $(Map<I,V> chart, I path) { return chart.get(path); } /** chart assignment */ protected static <V,T,U,N,I extends Path<T> & Indexer<List<Integer>,List<N>,? extends Path<U>>> V $(Map<I,V> chart, I path, V newvalue) { return chart.put(path, newvalue); } private <T,U,L> Iterable<T> _continue(boolean isLabeled, N node, List<N> sortedNodes, Path<U> relevantPath, T dummy) { Set<T> continuations = new THashSet<T>(); if (isLabeled) { for (N c : node.getChildren()) { int j = sortedNodes.indexOf(c); if (relevantPath!=null /*&& i>0*/ && relevantPath.getNode(1)!=c) continue; for (L l : ((HasLabeledEdges<N,L>)node).getLabels(c)) { if (relevantPath!=null) { L rL = ((Pair<N,L>)relevantPath.get(1)).getSecond(); if ((rL==null)!=(l==null) || !rL.equals(l)) continue; } continuations.add((T)new Pair<Integer,L>(j,l)); } } } else { for (N c : node.getChildren()) { if (relevantPath!=null /*&& i>0*/ && relevantPath.getNode(1)!=c) continue; Integer j = sortedNodes.indexOf(c); continuations.add((T)j); } } return continuations; } /** * * With history (hyperpath): * arrive(*, j) (+)= start(j) * arrive(i, j) (+)= arrive(H, i) (*) arc(i, j) * goal(j) (+)= arrive(I, j) (*) end(j) * * * arrive(*, *, j) (+)= start(j) * arrive(ii, i, j) (+)= arrive(H, ii, i) (*) arc(i, j) * goal(j) (+)= arrive(II, I, j) (*) end(j) * * etc. * * @param <V> Semiring value type * @param semr * @param order Markov order (1 reduces to a path problem) * @return * @throws GraphException */ private <V,T,U,I extends Path<T> & Indexer<List<Integer>,List<N>,? extends Path<U>>,L> List<I> arrive(boolean isLabeled, List<N> sortedNodes, Map<I,V> chart, Semirings.Semiring<V> semr, I item, Path<U> relevantPath, Subroutine edgeOperation) { // ii: indices of nodes such that we are computing the arrival at node ii[-1] given history ii[:-1] List<Integer> ii = item.indices(); int i = ii.get(ii.size()-1); N node = sortedNodes.get(i); assert (relevantPath==null || i==0 || relevantPath.getNode(0)==node); // sourceNodesSuffix = nodes indexed by ii[1:] //Path<U> path = ((Indexer<List<Integer>,List<N>,Path<U>>)item.getTail()).apply(sortedNodes); List<I> newItems = new ArrayList<I>(); for (T e : _continue(isLabeled, node, sortedNodes, relevantPath, item.getEnd())) { I newItem = (I)item.shift(e); // = ii[1:] + [j] Path<U> newItemFull = newItem.apply(sortedNodes); if (chart!=null) { // arrive(ii[1:],j) (+)= arrive(ii[:-1],ii[-1]) (*) featureScore(ii[1:],j) V edgeValue = (V)getEdgeValue(newItemFull, semr); assert edgeValue!=null; V lookup1 = $(chart,item); V prod = semr.times(lookup1, edgeValue); V lookup2 = $(chart,newItem); V sum = semr.plus(lookup2, prod); $(chart, newItem, sum); } else { // Execute an arbitrary function on the edge edgeOperation.$(newItemFull); } newItems.add(newItem); // add to agenda } return newItems; } protected <V,T,U,I extends Path<T> & Indexer<List<Integer>,List<N>,? extends Path<U>>> V arrivalsTo(List<N> sortedNodes, Map<I,V> subchart, Semirings.Semiring<V> semr, int iTarget, int markovOrder) { V v = semr.ozero(); for (I item : subchart.keySet()) { if ((Integer)item.getTarget()==iTarget) v = semr.plus(v, subchart.get(item)); } return v; } protected <V> Object getEdgeValue(Path<?> path, Semirings.Semiring<V> semr) { if (semr instanceof Semirings.PathSemiring<?,?>) { Set<Pair<W,? extends Path<N>>> s = new HashSet<Pair<W,? extends Path<N>>>(); s.add(new Pair<W,Path<N>>(getWeight(path), (Path<N>)path)); return s; } return getWeight(path); } protected W getWeight(Path<?> path) { N target = (N)path.getTarget(); W result = target.getWeight(path); assert result!=null; return result; } private static class TestNode extends RootedDAGNode<TestNode> implements HasWeightedHyperEdges<TestNode,Double> { protected Map<Path<TestNode>,Double> _weights; public TestNode(String lbl) { this(lbl, new HashMap<Path<TestNode>,Double>()); } public TestNode(String lbl, Map<Path<TestNode>,Double> incomingEdges) { _weights = incomingEdges; this.setLabelType(lbl); } @Override public Double getWeight(Path<?> path) { Double w = _weights.get(path); return w; } @Override public String toString() { return getLabelType(); } public static <W> void link(TestNode a, TestNode b, Double wt) throws GraphException { link(a, b); b._weights.put(new Path<TestNode>(Arrays.asList(new TestNode[]{a,b})), wt); } } private static class TestNodeLF extends RootedDAGNode<TestNodeLF> implements HasWeightedHyperEdges<TestNodeLF,LogFormula> { protected Map<Path<TestNodeLF>,LogFormula> _weights; public TestNodeLF(String lbl) { this(lbl, new HashMap<Path<TestNodeLF>,LogFormula>()); } public TestNodeLF(String lbl, Map<Path<TestNodeLF>,LogFormula> incomingEdges) { _weights = incomingEdges; this.setLabelType(lbl); } @Override public LogFormula getWeight(Path<?> path) { LogFormula w = _weights.get(path); return w; } @Override public String toString() { return getLabelType(); } public static void link(TestNodeLF a, TestNodeLF b, LogFormula wt) throws GraphException { link(a, b); b._weights.put(new Path<TestNodeLF>(Arrays.asList(new TestNodeLF[]{a,b})), wt); } } public static void main(String[] args) { try { // Unit testing for path computations with various semirings /* * R --[0.5]--> C --[0.6]--> D --[1.0]-->\ * |\ \ F --[1.0]-->\ * | \ \-[0.2]--> E --[1.0]-->/ \ * | \ \ S * | \ \--[1.0]-->\ /| * \ \--[0.35]------------------------> B -[1.0]->/ | * \ / * \---[0.15]--> A --[1.0]------------------------>/ * * Semiring Correct value * ---------------------------- * Max-Times max { .3, .1, .1, .35, .15 } = .35 * Plus-Times sum { .3, .1, .1, .35, .15 } = 1.0 (edge weights can be interpreted as probabilities) * Max-Plus max { 3.1, 2.7, 2.7, 1.35, 1.15 } = 3.1 * */ WeightedRootedHyperDAG<TestNode,Double> g = new WeightedRootedHyperDAG<TestNode, Double>(new TestNode("R")); /*try { g.initLogger(new FileWriter(new File("chartlog.csv"))); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ g._root.parents = new ArrayList<TestNode>(); // .getParents() was returning null TestNode sink = new TestNode("S"); sink.children = new ArrayList<TestNode>(); // .getChildren() was returning null TestNode.link(g._root, new TestNode("C"), 0.5); // R-->C TestNode b = new TestNode("B", new HashMap<Path<TestNode>,Double>()); TestNode.link(g._root, b, 0.35); // R-->B TestNode.link(g._root, new TestNode("A"), 0.15); // R-->A TestNode.link(g._root.children.get(0), new TestNode("D"), 0.6); // C-->D TestNode.link(g._root.children.get(0), new TestNode("E"), 0.2); // C-->E TestNode f = new TestNode("F"); TestNode.link(g._root.children.get(0).children.get(0), f, 1.0); // D-->F TestNode.link(g._root.children.get(0).children.get(1), f, 1.0); // E-->F TestNode.link(g._root.children.get(0).children.get(1), b, 1.0); // E-->B TestNode.link(f,sink,1.0); // F-->S TestNode.link(g._root.children.get(1), sink, 1.0); // B-->S TestNode.link(g._root.children.get(2), sink, 1.0); // A-->S System.out.println(g.computePath(new Semirings.MaxTimes())); System.out.println(g.computePath(new Semirings.PlusTimes())); System.out.println(g.computePath(new Semirings.MaxPlus())); System.out.println(g.computePath(new Semirings.MaxTimesPath<TestNode>(new Semirings.MaxTimes()))); System.out.println(g.computePath(new Semirings.PlusTimesPath<TestNode>(new Semirings.PlusTimes()))); System.out.println(g.computePath(new Semirings.MaxPlusPath<TestNode>(new Semirings.MaxPlus()))); // Now reconstruct the above graph, but with log equivalents for weights /* * Semiring Correct value (log space) Path yielding this value * -------------------------------------------------------------------- * Log-Max-Times log(.35) = -1.05 R --> B --> S * Log-Plus-Times log(1.0) = 0.0 all paths * Log-Max-Plus log(3.1) = 1.13 R --> C --> D --> F --> S */ g = new WeightedRootedHyperDAG<TestNode, Double>(new TestNode("R")); g._root.parents = new ArrayList<TestNode>(); // .getParents() was returning null sink = new TestNode("S"); sink.children = new ArrayList<TestNode>(); // .getChildren() was returning null TestNode.link(g._root, new TestNode("C", new HashMap<Path<TestNode>,Double>()), Math.log(0.5)); // R-->C b = new TestNode("B", new HashMap<Path<TestNode>,Double>()); TestNode.link(g._root, b, Math.log(0.35)); // R-->B TestNode.link(g._root, new TestNode("A", new HashMap<Path<TestNode>,Double>()), Math.log(0.15)); // R-->A TestNode.link(g._root.children.get(0), new TestNode("D", new HashMap<Path<TestNode>,Double>()), Math.log(0.6)); // C-->D TestNode.link(g._root.children.get(0), new TestNode("E", new HashMap<Path<TestNode>,Double>()), Math.log(0.2)); // C-->E f = new TestNode("F"); TestNode.link(g._root.children.get(0).children.get(0), f, 0.0); // D-->F TestNode.link(g._root.children.get(0).children.get(1), f, 0.0); // E-->F TestNode.link(g._root.children.get(0).children.get(1), b, 0.0); // E-->B TestNode.link(f,sink, 0.0); // F-->S TestNode.link(g._root.children.get(1), sink, 0.0); // B-->S TestNode.link(g._root.children.get(2), sink, 0.0); // A-->S System.out.println(g.computePath(new Semirings.LogMaxTimes())); System.out.println(g.computePath(new Semirings.LogPlusTimes())); System.out.println(g.computePath(new Semirings.LogMaxPlus())); System.out.println(g.computePath(new Semirings.MaxTimesPath<TestNode>(new Semirings.LogMaxTimes()))); System.out.println(g.computePath(new Semirings.PlusTimesPath<TestNode>(new Semirings.LogPlusTimes()))); System.out.println(g.computePath(new Semirings.MaxPlusPath<TestNode>(new Semirings.LogMaxPlus()))); // LogFormula version WeightedRootedHyperDAG<TestNodeLF,LogFormula> g2 = new WeightedRootedHyperDAG<TestNodeLF, LogFormula>(new TestNodeLF("R")); g2._root.parents = new ArrayList<TestNodeLF>(); // .getParents() was returning null TestNodeLF sink2 = new TestNodeLF("S"); sink2.children = new ArrayList<TestNodeLF>(); // .getChildren() was returning null TestNodeLF.link(g2._root, new TestNodeLF("C", new HashMap<Path<TestNodeLF>,LogFormula>()), new LogFormula(LDouble.convertToLogDomain(0.5))); // R-->C TestNodeLF b2 = new TestNodeLF("B", new HashMap<Path<TestNodeLF>,LogFormula>()); TestNodeLF.link(g2._root, b2, new LogFormula(LDouble.convertToLogDomain(0.35))); // R-->B TestNodeLF.link(g2._root, new TestNodeLF("A", new HashMap<Path<TestNodeLF>,LogFormula>()), new LogFormula(LDouble.convertToLogDomain(0.15))); // R-->A TestNodeLF.link(g2._root.children.get(0), new TestNodeLF("D", new HashMap<Path<TestNodeLF>,LogFormula>()), new LogFormula(LDouble.convertToLogDomain(0.6))); // C-->D TestNodeLF.link(g2._root.children.get(0), new TestNodeLF("E", new HashMap<Path<TestNodeLF>,LogFormula>()), new LogFormula(LDouble.convertToLogDomain(0.2))); // C-->E TestNodeLF f2 = new TestNodeLF("F"); TestNodeLF.link(g2._root.children.get(0).children.get(0), f2, new LogFormula(LDouble.convertToLogDomain(1.0))); // D-->F TestNodeLF.link(g2._root.children.get(0).children.get(1), f2, new LogFormula(LDouble.convertToLogDomain(1.0))); // E-->F TestNodeLF.link(g2._root.children.get(0).children.get(1), b2, new LogFormula(LDouble.convertToLogDomain(1.0))); // E-->B TestNodeLF.link(f2,sink2, new LogFormula(LDouble.convertToLogDomain(1.0))); // F-->S TestNodeLF.link(g2._root.children.get(1), sink2, new LogFormula(LDouble.convertToLogDomain(1.0))); // B-->S TestNodeLF.link(g2._root.children.get(2), sink2, new LogFormula(LDouble.convertToLogDomain(1.0))); // A-->S Semirings.LogFormulaBuilder lfb = new Semirings.LogFormulaBuilder() { @Override public LogFormula recruitFormula(IdentityElement ie) { return new LogFormula(ie); } @Override public LogFormula recruitFormula(Op op) { return new LogFormula(op); } }; // this is the Log-Plus-Times semiring, only encoding the computations as an arithmetic circuit LogFormula result = (LogFormula)g2.computePath(lfb); System.out.println(result.evaluate(null)); // should be 0 Path<TestNodeLF> nodesInPath = new Path<TestNodeLF>(); // path R-->C-->D-->F-->S nodesInPath.add(g2._root); // R nodesInPath.add(g2._root.children.get(0)); // C nodesInPath.add(g2._root.children.get(0).children.get(0)); // D nodesInPath.add(f2); // F nodesInPath.add(sink2); // S result = (LogFormula)g2.computeUnlabeledHyperpath(lfb, 1, nodesInPath); System.out.println(result.evaluate(null)); // should be log(0.3) = -1.20 } catch (GraphException e1) { e1.printStackTrace(); } } }