slib.graph.algo.extraction.validator.dag.ValidatorDAG Maven / Gradle / Ivy
/*
* Copyright or © or Copr. Ecole des Mines d'Alès (2012-2014)
*
* This software is a computer program whose purpose is to provide
* several functionalities for the processing of semantic data
* sources such as ontologies or text corpora.
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*/
package slib.graph.algo.extraction.validator.dag;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.openrdf.model.URI;
import org.openrdf.model.vocabulary.RDFS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import slib.graph.algo.accessor.GraphAccessor;
import slib.graph.algo.utils.Color;
import slib.graph.model.graph.G;
import slib.graph.model.graph.elements.E;
import slib.graph.model.graph.utils.Direction;
import slib.graph.model.graph.utils.WalkConstraint;
import slib.graph.utils.WalkConstraintGeneric;
import slib.graph.utils.WalkConstraintUtils;
import slib.utils.ex.SLIB_Ex_Critic;
import slib.utils.impl.SetUtils;
/**
* Used to validate if a graph is directed and acyclic (DAG)
*
* @author Sébastien Harispe
*/
public class ValidatorDAG {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
G graph;
WalkConstraint wc;
HashMap verticesColors;
boolean valid;
E lastEdge;
Path currentPath;
/*---------------------------------------------------------------------*
* Algorithm
*---------------------------------------------------------------------*/
/**
*
* @param graph
* @param startingURIs
* @param wc
* @return true if the graph is a DAG
* @throws SLIB_Ex_Critic
*/
public boolean isDag(G graph, Set startingURIs, WalkConstraint wc) throws SLIB_Ex_Critic {
this.wc = wc;
this.graph = graph;
this.verticesColors = new HashMap();
valid = true;
logger.debug("Cheking DAG property of : " + graph.getURI());
logger.debug("WalkConstraint : " + wc);
if (startingURIs == null || startingURIs.isEmpty()) {
return false;
}
logger.debug("starting nodes : " + startingURIs.size());
if (startingURIs.size() < 10) {
logger.debug("starting vertices : " + startingURIs);
}
currentPath = new Path();
for (URI rootUri : startingURIs) {
if (!graph.containsVertex(rootUri)) {
throw new SLIB_Ex_Critic("Vertex '" + rootUri + "' not found in " + graph.getURI());
}
if (valid) {
performDFS(rootUri);
}
}
logger.info("isDag : " + valid);
if (!valid) {
logger.info("current path :" + currentPath.toString());
logger.info("Cycle detected adding : " + lastEdge + " to path");
}
return valid;
}
private void performDFS(URI v) {
if (!valid) {
return;
}
if (!verticesColors.containsKey(v)) {
verticesColors.put(v, Color.ORANGE);
Set edges = graph.getE(v, wc);
for (E e : edges) {
if (!valid) {
return;
}
URI target = e.getTarget();
if (target.equals(v)) { // IN
target = e.getSource();
}
if (verticesColors.get(target) != Color.RED) {
currentPath.addEdge(e);
lastEdge = e;
performDFS(target);
}
}
if (!valid) {
return;
}
currentPath.removeLastEdge();
verticesColors.put(v, Color.RED);
} else if (verticesColors.get(v) == Color.ORANGE) {
valid = false;
}
}
/*---------------------------------------------------------------------*
* Utils
*---------------------------------------------------------------------*/
/**
* Check if the underlying graph defined by the edges of the given edge
* types and build using a traversal starting from the given root node is a
* DAG. shortcut of {@link ValidatorDAG#isDag(G, WalkConstraint)} only
* considering the given set of edge types
*
* @param graph the graph on which the evaluation has to be made
* @param rootURI
* @param wc
* @return true if the the (underlying) graph reduction is a DAG
* @throws SLIB_Ex_Critic
*
*/
public boolean isDag(G graph, URI rootURI, WalkConstraint wc) throws SLIB_Ex_Critic {
return isDag(graph, SetUtils.buildSet(rootURI), wc);
}
/**
*
* @param graph
* @return true if the graph contains a taxonomic graph.
* @throws SLIB_Ex_Critic
*/
public boolean containsTaxonomicDag(G graph) throws SLIB_Ex_Critic {
WalkConstraint wct = new WalkConstraintGeneric(RDFS.SUBCLASSOF, Direction.IN);
return isDag(graph, wct);
}
/**
* Check if the underlying graph defined by given {@link WalkConstraint} is
* a DAG. shortcut of {@link ValidatorDAG#isDag(G, WalkConstraint)}
* considering the given edge types and the root vertices according to the
* inverse of the specified edge types as root (see
* {@link ValidatorDAG#getDAGRoots(G, WalkConstraint)})
*
* @param graph the graph on which the evaluation has to be made
* @param wc
* @return true if the the (underlying) graph reduction is a DAG
*
* @throws SLIB_Ex_Critic
*/
public boolean isDag(G graph, WalkConstraint wc) throws SLIB_Ex_Critic {
logger.debug("Check DAG property of the graph " + graph.getURI() + " considering the walkconstraint " + wc);
Set startingNodes = getDAGRoots(graph, WalkConstraintUtils.getInverse(wc, false));
logger.info("Starting process from " + startingNodes.size() + " vertices");
if(graph.getE().isEmpty()){
logger.info("No edge");
}
else if (startingNodes.isEmpty()) // No root No Dag
{
logger.debug("No roots have been detected...");
logger.debug("DAG = false");
return false;
}
else if(!isDag(graph, startingNodes, wc)){
return false;
}
return true;
}
/**
* Root vertices (terminal vertices) are those of type CLASS which respect
* the following restrictions :
- must not contains an edges of
* the given types and direction
*
* Do not check if the graph is a DAG
*
* @param g the graph on which the root vertices need to be retrieve
* @param wc
* @return The set of vertices matching the predefined conditions
*/
public Set getDAGRoots(G g, WalkConstraint wc) {
Set classes = GraphAccessor.getClasses(g);
Set roots = new HashSet();
for (URI v : classes) {
if (g.getV(v, wc).isEmpty()) {
roots.add(v);
}
}
return roots;
}
/**
* Check if the given graph contains a underlying taxonomic graph with a
* unique root.
*
* @param g the graph on which the test is performed
* @return true if the graph contains a unique underlying rooted taxonomic
* graph
*
* @throws SLIB_Ex_Critic
*/
public boolean containsTaxonomicDagWithUniqueRoot(G g) throws SLIB_Ex_Critic {
WalkConstraintGeneric wc = new WalkConstraintGeneric(RDFS.SUBCLASSOF, Direction.OUT);
Set roots = getDAGRoots(g, wc);
logger.info("Number of roots " + roots.size());
logger.debug("Root(s): " + roots);
if (roots.size() == 1) {
isDag(g, roots.iterator().next(), new WalkConstraintGeneric(RDFS.SUBCLASSOF, Direction.IN));
} else {
valid = false;
}
logger.debug("isRootedTaxonomicDag (" + roots.size() + " root(s)) valid " + valid);
return valid;
}
/**
* Return the unique vertex rooting the underlying taxonomic graph. Do not
* check if the taxonomic graph is a DAG but throw an error if the taxonomic
* graph contains multiple roots.
*
* @param g the graph
* @return the unique vertex which roots the taxonomic graph
*
* @throws SLIB_Ex_Critic if the underlying taxonomic graph of the
* given graph contains multiple roots.
*
*/
public URI getUniqueTaxonomicRoot(G g) throws SLIB_Ex_Critic {
WalkConstraintGeneric wcTax = new WalkConstraintGeneric(RDFS.SUBCLASSOF, Direction.OUT);
Set roots = getDAGRoots(g, wcTax);
if (roots.size() != 1) {
throw new SLIB_Ex_Critic("Multiple roots detected in the underlying taxonomic graph of graph " + g.getURI());
}
return roots.iterator().next();
}
/**
* Return the vertices which root the taxonomic graph.
*
* @param g
* @return the vertices which can be considered as a root, i.e. all the
* vertices which are not subsumed by an other vertex through a taxonomic
* relationship.
*/
public Set getTaxonomicRoots(G g) {
return getDAGRoots(g, new WalkConstraintGeneric(RDFS.SUBCLASSOF, Direction.OUT));
}
/**
* Test if the given URI corresponds to a root of the graph build according
* to the specified edge types.
*
* @param g the graph to consider
* @param rootURI the URI to test
* @param wc the edge type to consider only OUT direction is considered
* @return true if the graph defined by the specified constraint is rooted
* by the given URI
*/
public boolean containsRootedDagRoot(G g, URI rootURI, WalkConstraint wc) {
for (URI v : getDAGRoots(g, wc)) {
if (v.equals(rootURI)) {
return true;
}
}
return false;
}
/**
* @param g
* @param root
* @return true if the graph contains a taxonomic graph which is rooted by a
* unique vertex.
* @throws SLIB_Ex_Critic
*/
public boolean isUniqueRootedTaxonomicDag(G g, URI root) throws SLIB_Ex_Critic {
WalkConstraint wc = new WalkConstraintGeneric(RDFS.SUBCLASSOF, Direction.IN);
return isUniqueRootedDagRoot(g, root, wc);
}
/**
* Do not check if the graph is a DAG
*
* @param g
* @param root
* @param wc
* @return true if the graph is a DAG and rooted by a unique vertex.
* @throws SLIB_Ex_Critic
*/
public boolean isUniqueRootedDagRoot(G g, URI root, WalkConstraint wc) throws SLIB_Ex_Critic {
if (isDag(g, wc)) {
Set roots = getDAGRoots(g, WalkConstraintUtils.getInverse(wc, false));
logger.debug("roots: " + roots);
if (roots.size() == 1 && roots.iterator().next().equals(root)) {
return true;
}
}
return false;
}
/**
*
* @return the last edge which has been processed during the treatment.
*/
public E getLastEdge() {
return lastEdge;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy