mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-15 06:49:56 +00:00
added a working test with inner/outer graph
Change-Id: Ia1b4f6ed3ed96880e4d78b72373c93d2d34e99cd
This commit is contained in:
1 parent
1b15154bbc
commit
eb39573ce2
3 files changed
+142
-2
No files matched your search
+122
-1
@@ -1,9 +1,13 @@
|
||||
package com.sap.sailing.windestimation.aggregator.msthmm;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -11,7 +15,10 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.windestimation.aggregator.msthmm.graph.DijkstraShortestPathFinderImpl;
|
||||
import com.sap.sailing.windestimation.aggregator.msthmm.graph.ElementAdjacencyQualityMetric;
|
||||
import com.sap.sailing.windestimation.aggregator.msthmm.graph.ElementWithQuality;
|
||||
import com.sap.sailing.windestimation.aggregator.msthmm.graph.GroupOutOfWhichToPickTheBestElement;
|
||||
import com.sap.sailing.windestimation.aggregator.msthmm.graph.InnerGraphSuccessorSupplier;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.impl.NamedImpl;
|
||||
@@ -38,6 +45,37 @@ public class DijkstraTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static class CompositeTreeNode implements GroupOutOfWhichToPickTheBestElement<Node, CompositeTreeNode> {
|
||||
private final CompositeTreeNode parent;
|
||||
private final Node[] elements;
|
||||
private final Set<CompositeTreeNode> children;
|
||||
|
||||
public CompositeTreeNode(CompositeTreeNode parent, Node... elements) {
|
||||
this.parent = parent;
|
||||
this.elements = elements;
|
||||
this.children = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompositeTreeNode getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<CompositeTreeNode> getChildren() {
|
||||
return Collections.unmodifiableCollection(children);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Node> getElements() {
|
||||
return Arrays.asList(elements);
|
||||
}
|
||||
|
||||
public boolean addChild(CompositeTreeNode child) {
|
||||
return this.children.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
dijkstra = new DijkstraShortestPathFinderImpl<DijkstraTest.Node>();
|
||||
@@ -47,6 +85,77 @@ public class DijkstraTest {
|
||||
edgeQuality = new HashMap<>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCompositeTree() {
|
||||
// set up overarching tree structure:
|
||||
// root
|
||||
// |
|
||||
// level1
|
||||
// /\
|
||||
// level2Left level2Right
|
||||
// | |
|
||||
// level3Left level3Right
|
||||
// Each of these tree nodes contains two inner graph nodes
|
||||
final CompositeTreeNode root = new CompositeTreeNode(/* parent */ null, new Node("Root1", 0.5), new Node("Root2", 0.5));
|
||||
final CompositeTreeNode level1 = new CompositeTreeNode(/* parent */ root, new Node("L1_1", 0.5), new Node("L1_2", 0.5));
|
||||
root.addChild(level1);
|
||||
final CompositeTreeNode level2Left = new CompositeTreeNode(/* parent */ level1, new Node("L2L1", 0.1), new Node("L2L2", 0.9));
|
||||
level1.addChild(level2Left);
|
||||
final CompositeTreeNode level2Right = new CompositeTreeNode(/* parent */ level1, new Node("L2R1", 0.9), new Node("L2R2", 0.1));
|
||||
level1.addChild(level2Right);
|
||||
final CompositeTreeNode level3Left = new CompositeTreeNode(/* parent */ level2Left, new Node("L3L1", 0.1), new Node("L3L2", 0.9));
|
||||
level2Left.addChild(level3Left);
|
||||
final CompositeTreeNode level3Right = new CompositeTreeNode(/* parent */ level2Right, new Node("L3R1", 0.9), new Node("L3R2", 0.1));
|
||||
level2Right.addChild(level3Right);
|
||||
// now set up the qualities of the edges connecting the inner nodes; simple model:
|
||||
// staying on the same inner node index is good (quality 0.9), changing node index is bad (quality 0.1);
|
||||
// this way, at the junction represented by level1 the path coming up from level2Left with its
|
||||
// preferred __L2 nodes should end up in L1_2, whereas the path coming up from level2Right with its
|
||||
// preferred __R1 nodes should end up in L1_1.
|
||||
setEdgeQuality(level3Left, 0, level2Left, 0, 0.9);
|
||||
setEdgeQuality(level3Left, 0, level2Left, 1, 0.1);
|
||||
setEdgeQuality(level3Left, 1, level2Left, 0, 0.1);
|
||||
setEdgeQuality(level3Left, 1, level2Left, 1, 0.9);
|
||||
setEdgeQuality(level3Right, 0, level2Right, 0, 0.9);
|
||||
setEdgeQuality(level3Right, 0, level2Right, 1, 0.1);
|
||||
setEdgeQuality(level3Right, 1, level2Right, 0, 0.1);
|
||||
setEdgeQuality(level3Right, 1, level2Right, 1, 0.9);
|
||||
setEdgeQuality(level2Left, 0, level1, 0, 0.9);
|
||||
setEdgeQuality(level2Left, 0, level1, 1, 0.1);
|
||||
setEdgeQuality(level2Left, 1, level1, 0, 0.1);
|
||||
setEdgeQuality(level2Left, 1, level1, 1, 0.9);
|
||||
setEdgeQuality(level2Right, 0, level1, 0, 0.9);
|
||||
setEdgeQuality(level2Right, 0, level1, 1, 0.1);
|
||||
setEdgeQuality(level2Right, 1, level1, 0, 0.1);
|
||||
setEdgeQuality(level2Right, 1, level1, 1, 0.9);
|
||||
setEdgeQuality(level1, 0, root, 0, 0.9);
|
||||
setEdgeQuality(level1, 0, root, 1, 0.1);
|
||||
setEdgeQuality(level1, 1, root, 0, 0.1);
|
||||
setEdgeQuality(level1, 1, root, 1, 0.9);
|
||||
// now have the inner nodes connected among each other according to the overarching tree structure...
|
||||
final InnerGraphSuccessorSupplier<Node, CompositeTreeNode> successorSupplier = new InnerGraphSuccessorSupplier<>(
|
||||
()->root, nodeName->new Node(nodeName, 1.0));
|
||||
// the edge qualities for the additional artificial nodes will all default to 1.0
|
||||
// ...and solve the inner graph's shortest path problem, once for each artificial leaf:
|
||||
final Iterable<Node> bestPathStartingAtLevel3Left = new DijkstraShortestPathFinderImpl<Node>().getShortestPath(
|
||||
successorSupplier.getArtificialLeaf(level3Left), successorSupplier.getArtificialRoot(),
|
||||
successorSupplier, getEdgeQualitySupplier());
|
||||
final Iterable<Node> bestPathStartingAtLevel3Right = new DijkstraShortestPathFinderImpl<Node>().getShortestPath(
|
||||
successorSupplier.getArtificialLeaf(level3Right), successorSupplier.getArtificialRoot(),
|
||||
successorSupplier, getEdgeQualitySupplier());
|
||||
// We expect the two solutions to provide different best inner nodes for the converging level1 node.
|
||||
// For the indices, note that 0 denotes the artificial leaf node, so index 1 denotes the inner
|
||||
// level3 node selected, index 2 the inner level2 node, and index 3 the inner level1 node which is
|
||||
// what we'd like to compare:
|
||||
assertNotSame(Util.get(bestPathStartingAtLevel3Left, 3), Util.get(bestPathStartingAtLevel3Right, 3));
|
||||
}
|
||||
|
||||
private void setEdgeQuality(CompositeTreeNode fromLevel, int fromInnerNodeZeroBased, CompositeTreeNode toLevel,
|
||||
int toInnerNodeZeroBased, double quality) {
|
||||
edgeQuality.put(new Pair<>(Util.get(fromLevel.getElements(), fromInnerNodeZeroBased),
|
||||
Util.get(toLevel.getElements(), toInnerNodeZeroBased)), quality);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicDijkstra() {
|
||||
edge(startNode, endNode, 1.0);
|
||||
@@ -57,7 +166,19 @@ public class DijkstraTest {
|
||||
|
||||
private Iterable<Node> getShortestPath() {
|
||||
return dijkstra.getShortestPath(startNode, endNode,
|
||||
n -> successors.get(n), (n1, n2) -> edgeQuality.get(new Pair<>(n1, n2)));
|
||||
n -> successors.get(n), getEdgeQualitySupplier());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link ElementAdjacencyQualityMetric} that etrieves the quality from {@link #edgeQuality} and defaults
|
||||
* to {@code 1.0} if no other quality definition is found in {@link #edgeQuality} for the node pair provided.
|
||||
*/
|
||||
private ElementAdjacencyQualityMetric<Node> getEdgeQualitySupplier() {
|
||||
return (n1, n2) -> {
|
||||
final Pair<Node, Node> nodes = new Pair<>(n1, n2);
|
||||
final Double quality = edgeQuality.get(new Pair<>(n1, n2));
|
||||
return quality == null ? 1.0 : quality;
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+19
-1
@@ -77,10 +77,11 @@ implements Function<T, Iterable<T>> {
|
||||
innerChildElements = Collections.singleton(artificialLeaf);
|
||||
} else {
|
||||
final Set<T> allInnerChildNodes = new HashSet<>();
|
||||
for (final GroupOutOfWhichToPickTheBestElement<T, G> child : node.getChildren()) {
|
||||
for (final G child : node.getChildren()) {
|
||||
for (final T innerChildElement : child.getElements()) {
|
||||
allInnerChildNodes.add(innerChildElement);
|
||||
}
|
||||
addAllEdges(child);
|
||||
}
|
||||
innerChildElements = allInnerChildNodes;
|
||||
}
|
||||
@@ -95,4 +96,21 @@ implements Function<T, Iterable<T>> {
|
||||
public Iterable<T> apply(T t) {
|
||||
return successors.get(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the artificial single root node that is parent of all of the
|
||||
* {@link GroupOutOfWhichToPickTheBestElement#getElements() elements} of the {@link Tree#getRoot() root} of
|
||||
* the {@link Tree} passed to this object's constructor.
|
||||
*/
|
||||
public T getArtificialRoot() {
|
||||
return artificialRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the artificial leaf node for which all {@link GroupOutOfWhichToPickTheBestElement#getElements() elements}
|
||||
* of the {@code treeLeafNode} are configured as successors.
|
||||
*/
|
||||
public T getArtificialLeaf(G treeLeafNode) {
|
||||
return artificialLeaves.get(treeLeafNode);
|
||||
}
|
||||
}
|
||||
+1
@@ -7,6 +7,7 @@ package com.sap.sailing.windestimation.aggregator.msthmm.graph;
|
||||
*
|
||||
* @param <TN>
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Tree<TN extends TreeNode<? extends TN>> {
|
||||
TN getRoot();
|
||||
}
|
||||
Reference in new issue
Block a user