Java tutorial
/******************************************************************************* * Copyright (C) 2013 University of Waikato, Hamilton, New Zealand. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * Sam Sarjant - initial API and implementation ******************************************************************************/ package graph.module; import graph.core.CommonConcepts; import graph.core.DAGEdge; import graph.core.DAGNode; import graph.core.Node; import graph.core.OntologyFunction; import graph.inference.CommonQuery; import graph.inference.QueryObject; import graph.inference.VariableNode; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import org.apache.commons.collections4.CollectionUtils; import util.collection.MultiMap; public class DepthModule extends DAGModule<Collection<DAGNode>> { private static final long serialVersionUID = 7586206693104940128L; public static final String DEPTH_PROPERTY = "depth"; private boolean depthCalculated_ = false; private MultiMap<Integer, DAGNode> depthMap_; private boolean incrementalSupported_ = false; public DepthModule() { super(); } private Collection<Node> getMinimumParents(DAGNode node) { // Check for rewrite RewriteOfModule rewriteModule = (RewriteOfModule) dag_.getModule(RewriteOfModule.class); if (rewriteModule != null) node = rewriteModule.getRewrite(node); QueryModule querier = (QueryModule) dag_.getModule(QueryModule.class); Collection<Node> minParents = new ArrayList<>(); // Add genls minParents.addAll(checkParentRelationship(node, CommonConcepts.GENLS, querier)); // Add isa minParents.addAll(checkParentRelationship(node, CommonConcepts.ISA, querier)); // Add genlPreds minParents.addAll(checkParentRelationship(node, CommonConcepts.GENLPREDS, querier)); // Add genlMt minParents.addAll(checkParentRelationship(node, CommonConcepts.GENLMT, querier)); // If function if (node instanceof OntologyFunction) { // Add resultIsa minParents.addAll(querier.functionResults((OntologyFunction) node, CommonConcepts.RESULT_ISA)); // Add resultGenls minParents.addAll(querier.functionResults((OntologyFunction) node, CommonConcepts.RESULT_GENL)); } minParents.remove(node); return CommonQuery.minGeneralFilter(minParents, dag_); } /** * Searches for all assertions with a given relationship and node placement * and returns all second arg values for found assertions. * * @param node * The node to search for. * @param relationship * The relationship for the node. * @param querier * The query module. * @return The collection of all second arguments for assertions with * relationship and arg as the first and second args respectively. */ @SuppressWarnings("unchecked") private Collection<Node> checkParentRelationship(DAGNode node, CommonConcepts relationship, QueryModule querier) { VariableNode x = new VariableNode("?X"); QueryObject qo = new QueryObject(relationship.getNode(dag_), node, x); querier.applyModule(CommonConcepts.ASSERTED_SENTENCE.getNodeName(), qo); Collection<Node> results = QueryModule.parseResultsFromSubstitutions(x, qo.getResults()); if (results != null) return results; return CollectionUtils.EMPTY_COLLECTION; } /** * Recursively calculate the node depth. * * @param node * The node to calculate depth for. * @param seen * If the node has been seen already in this calculation. * @return The minimum depth of the above node. */ private int processNode(DAGNode node, HashSet<Integer> seen) { if (node.equals(CommonConcepts.THING.getNode(dag_))) { dag_.addProperty(node, DEPTH_PROPERTY, "0"); depthMap_.put(0, node); return 0; } if (seen.contains(node.getID())) return 0; seen.add(node.getID()); int depth = 1; String depthStr = node.getProperty(DEPTH_PROPERTY); if (depthStr == null || depthStr.equals("-1")) { Collection<Node> minCol = getMinimumParents(node); for (Node superGenls : minCol) { depth = Math.max(processNode((DAGNode) superGenls, new HashSet<>(seen)) + 1, depth); } dag_.addProperty(node, DEPTH_PROPERTY, depth + ""); depthMap_.put(depth, node); } else depth = Integer.parseInt(depthStr); return depth; } @Override public void clear() { depthCalculated_ = false; depthMap_.clear(); } @Override public boolean addEdge(DAGEdge edge) { if (!(edge instanceof DAGEdge) || !depthCalculated_) return true; if (!incrementalSupported_) { depthCalculated_ = false; return true; } DAGNode edgePred = (DAGNode) edge.getNodes()[0]; if (edgePred.equals(CommonConcepts.GENLS.getNode(dag_)) || edgePred.equals(CommonConcepts.GENLPREDS.getNode(dag_)) || edgePred.equals(CommonConcepts.GENLMT.getNode(dag_)) || edgePred.equals(CommonConcepts.ISA.getNode(dag_))) { if (!(edge.getNodes()[1] instanceof DAGNode)) return true; DAGNode subjectNode = (DAGNode) edge.getNodes()[1]; String oldStr = subjectNode.getProperty(DEPTH_PROPERTY); int oldDepth = (oldStr == null) ? -1 : Integer.parseInt(oldStr); int newDepth = processNode(subjectNode, new HashSet<Integer>()); if (newDepth != oldDepth) { // TODO Propagate the changes down. } } return true; } @Override public Collection<DAGNode> execute(Object... args) throws IllegalArgumentException, ModuleException { // Return all nodes at depth arg if (args[0] instanceof Integer) return getNodesAtDepth((Integer) args[0]); return null; } public Collection<DAGNode> getNodesAtDepth(int depth) { return depthMap_.get(depth); } @Override public boolean initialisationComplete(Collection<DAGNode> nodes, Collection<DAGEdge> edges, boolean forceRebuild) { if (depthCalculated_ && !forceRebuild) return false; // Compute the depths of each node in the graph System.out.print("Calculating node depths... "); depthMap_ = MultiMap.createConcurrentHashSetMultiMap(); int count = 0; int tenPercent = nodes.size() / 10; // TODO Embarrassingly parallel. for (DAGNode node : nodes) { count++; processNode(node, new HashSet<Integer>()); if (count % tenPercent == 0) System.out.print((count / tenPercent * 10) + "% "); } System.out.println("Depth calculation complete!"); depthCalculated_ = true; return true; } @Override public boolean removeEdge(DAGEdge edge) { if (!incrementalSupported_) { depthCalculated_ = false; return true; } return true; } @Override public Collection<String> getPertinentProperties() { Collection<String> props = new ArrayList<String>(1); props.add(DEPTH_PROPERTY); return props; } @Override public void disableCached() { depthCalculated_ = false; depthMap_ = null; } }