//The MIT License // // Copyright (c) 2004 Mindswap Research Group, University of Maryland, College Park // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. package org.mindswap.swoop.utils; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeModel; import javax.swing.tree.TreeNode; import org.mindswap.swoop.SwoopModel; import org.mindswap.swoop.reasoner.SwoopReasoner; import org.semanticweb.owl.io.vocabulary.OWLVocabularyAdapter; import org.semanticweb.owl.model.OWLClass; import org.semanticweb.owl.model.OWLClassAxiom; import org.semanticweb.owl.model.OWLDataProperty; import org.semanticweb.owl.model.OWLDescription; import org.semanticweb.owl.model.OWLDisjointClassesAxiom; import org.semanticweb.owl.model.OWLEquivalentClassesAxiom; import org.semanticweb.owl.model.OWLObjectProperty; import org.semanticweb.owl.model.OWLOntology; import org.semanticweb.owl.model.OWLProperty; import org.semanticweb.owl.model.OWLSubClassAxiom; /** * @author Aditya */ public class SwoopStatistics { SwoopModel swoopModel; public static String ONTOLOGY = "O"; public static String UNSATISFIABLE_CLASSES = "Unsatisfiable"; public static String NO_GCI = "GCIs"; public static String NO_SUBSUMPTIONS = "Subsumptions"; public static String NO_DISJOINT = "Disjoints"; public static String NO_TRANSITIVE = "Transitive"; public static String NO_SYMMETRIC = "Symmetric"; public static String NO_FUNCTIONAL = "Functional"; public static String NO_INVFUNCTIONAL = "Inverse-Functional"; public static String NO_INVERSE = "Inverses"; public static String MAX_DEPTH_CLASS_TREE = "Deepest SubTree [C]"; public static String MIN_DEPTH_CLASS_TREE = "Shallowest SubTree [C]"; public static String AVG_DEPTH_CLASS_TREE = "8"; public static String MAX_DEPTH_PROP_TREE = "Deepest SubTree [P]"; public static String MIN_DEPTH_PROP_TREE = "Shallowest SubTree [P]"; public static String AVG_DEPTH_PROP_TREE = "11"; public static String MAX_BRANCHING_FACTOR = "Most Children [C]"; public static String MIN_BRANCHING_FACTOR = "Fewest Children [C]"; public static String AVG_BRANCHING_FACTOR = ""; public static String MULTIPLE_INHERITANCE_CLASS = "Mult. Inheritance [C]"; public static String MULTIPLE_INHERITANCE_PROP = "Mult. Inheritance [P]"; public SwoopStatistics(SwoopModel model) { this.swoopModel = model; } public HashMap computeStatistics(OWLOntology ontology) { SwoopReasoner reasoner = swoopModel.getReasoner(); HashMap stats = new HashMap(); // stats.put(this.ONTOLOGY, ontology); // don't need this Set gci = new HashSet(); Set disj = new HashSet(); try { // class axiom types for (Iterator iter = ontology.getClassAxioms().iterator(); iter.hasNext();) { OWLClassAxiom axiom = (OWLClassAxiom) iter.next(); if (axiom instanceof OWLSubClassAxiom) { OWLSubClassAxiom subAxiom = (OWLSubClassAxiom) axiom; if (!(subAxiom.getSubClass() instanceof OWLClass)) gci.add(axiom); } else if (axiom instanceof OWLEquivalentClassesAxiom) { OWLEquivalentClassesAxiom equAxiom = (OWLEquivalentClassesAxiom) axiom; for (Iterator iter2=equAxiom.getEquivalentClasses().iterator(); iter2.hasNext();) { OWLDescription desc = (OWLDescription) iter2.next(); if (!(desc instanceof OWLClass)) gci.add(axiom); } } else if (axiom instanceof OWLDisjointClassesAxiom) disj.add(axiom); } stats.put(this.NO_GCI, gci); stats.put(this.NO_DISJOINT, disj); // prop attribs Set props = ontology.getObjectProperties(); props.addAll(ontology.getDataProperties()); Set tran = new HashSet(); Set symm = new HashSet(); Set invf = new HashSet(); Set func = new HashSet(); Set inv = new HashSet(); Set multP = new HashSet(); for (Iterator iter = props.iterator(); iter.hasNext();) { OWLProperty prop = (OWLProperty) iter.next(); // check multiple inheritance for props if (prop!=null && reasoner.getOntology()!=null && reasoner.getOntology().equals(ontology) && reasoner.superPropertiesOf(prop).size()>1) multP.add(prop); if (prop instanceof OWLObjectProperty) { if (((OWLObjectProperty) prop).isFunctional(ontology)) func.add(prop); if (((OWLObjectProperty) prop).isInverseFunctional(ontology)) invf.add(prop); if (((OWLObjectProperty) prop).isTransitive(ontology)) tran.add(prop); if (((OWLObjectProperty) prop).isSymmetric(ontology)) symm.add(prop); if (((OWLObjectProperty) prop).getInverses(ontology).size()>0) inv.add(prop); } else { if (((OWLDataProperty) prop).isFunctional(ontology)) func.add(prop); } } stats.put(this.MULTIPLE_INHERITANCE_PROP, multP); stats.put(this.NO_FUNCTIONAL, func); stats.put(this.NO_INVFUNCTIONAL, invf); stats.put(this.NO_TRANSITIVE, tran); stats.put(this.NO_SYMMETRIC, symm); stats.put(this.NO_INVERSE, inv); // tree specifics (using current SwoopReasoner and current Class/Prop trees) String minCl="?", maxCl="?", avgCl="?"; // class depth String minBf="?", maxBf="?", avgBf="?"; // braching factor Set maxCSet = new HashSet(); Set minCSet = new HashSet(); Set maxBfSet = new HashSet(); Set minBfSet = new HashSet(); OWLOntology cTreeOfOnt = swoopModel.getFrame().termDisplay.getClassTreeOfOntology(); if (cTreeOfOnt!=null && cTreeOfOnt.equals(ontology)) { int minC = Integer.MAX_VALUE, maxC = 0; int minB = Integer.MAX_VALUE, maxB = 0; // B for branching factor float avgC = 0; float avgB = 0; HashMap cache = new HashMap(); HashMap bcache = new HashMap(); if (swoopModel.getFrame().termDisplay.getTrees()[0]!=null) { TreeModel cTreeModel = swoopModel.getFrame().termDisplay.getTrees()[0].getModel(); DefaultMutableTreeNode cRoot = (DefaultMutableTreeNode) cTreeModel.getRoot(); // compute total subsumptions int subsumptions = 0; // for (int i = 0; i<cRoot.getChildCount(); i++) { subsumptions = getDescendentCount(cRoot) - cRoot.getChildCount(); // } stats.put(this.NO_SUBSUMPTIONS, String.valueOf(subsumptions)); Enumeration depthFirstEnum = cRoot.depthFirstEnumeration(); int numLeaves = 0; int numNonLeaves = 0; while ( depthFirstEnum.hasMoreElements() ) { DefaultMutableTreeNode node = (DefaultMutableTreeNode)depthFirstEnum.nextElement(); // do not count the depth/branching factor of unsatisfiable classes TreeNode [] path = node.getPath(); if ( path.length > 1 ) { DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode)path[1]; Set concepts = (Set)dmtn.getUserObject(); String n = OWLVocabularyAdapter.OWL; OWLClass concept = (OWLClass)concepts.iterator().next(); // get first element if ( concept.getURI().toString().equals( OWLVocabularyAdapter.OWL + "Nothing")) continue; } if ( node.isLeaf() ) // if leaf, count depth { numLeaves++; int depth = path.length - 1; // path.length - 1 = number of links from root to leaf if ( depth > 0) this.cacheDepth(cache, String.valueOf(depth), (Set)((DefaultMutableTreeNode)path[1]).getUserObject()); if ( depth == 0 ) this.cacheDepth(cache, String.valueOf(depth), (Set)(cRoot).getUserObject()); if ( depth > maxC) maxC = depth; if ( depth < minC ) minC = depth; avgC = avgC + depth; } else // not a leaf node, count branching factor { numNonLeaves++; int numChildren = node.getChildCount(); this.cacheDepth(bcache, String.valueOf(numChildren), (Set)node.getUserObject()); if ( numChildren > maxB) maxB = numChildren; if ( numChildren < minB ) minB = numChildren; avgB = avgB + numChildren; } } avgC = avgC / numLeaves; avgB = avgB / numNonLeaves; maxCl = String.valueOf(maxC); if (minC == Integer.MAX_VALUE) minC = 0; minCl = String.valueOf(minC); avgCl = String.valueOf(avgC); maxBf = String.valueOf(maxB); if (minB == Integer.MAX_VALUE) minB = 0; minBf = String.valueOf(minB); avgBf = String.valueOf(avgB); // get element set corresponding to min/max maxCSet = (HashSet) cache.get(maxCl); minCSet = (HashSet) cache.get(minCl); maxBfSet = (HashSet) bcache.get( maxBf ); minBfSet = (HashSet) bcache.get( minBf ); } } // class tree depth if ((avgCl.length()>4) && ( avgCl.indexOf(".") != -1)) avgCl = avgCl.substring(0, avgCl.indexOf(".") + 2); stats.put(this.AVG_DEPTH_CLASS_TREE, avgCl); List minCList = new ArrayList(); minCList.add(minCl); minCList.add(minCSet); stats.put(this.MIN_DEPTH_CLASS_TREE, minCList); List maxCList = new ArrayList(); maxCList.add(maxCl); maxCList.add(maxCSet); stats.put(this.MAX_DEPTH_CLASS_TREE, maxCList); // branching factor if ((avgBf.length()>4) && ( avgBf.indexOf(".") != -1)) avgBf = avgBf.substring(0, avgBf.indexOf(".") + 2); stats.put(this.AVG_BRANCHING_FACTOR, avgBf); List minBList = new ArrayList(); minBList.add(minBf); minBList.add(minBfSet); stats.put(this.MIN_BRANCHING_FACTOR, minBList); List maxBList = new ArrayList(); maxBList.add(maxBf); maxBList.add(maxBfSet); stats.put(this.MAX_BRANCHING_FACTOR, maxBList); String minPr="?", maxPr="?", avgPr="?"; Set maxPSet = new HashSet(); Set minPSet = new HashSet(); OWLOntology pTreeOfOnt = swoopModel.getFrame().termDisplay.getPropTreeOfOntology(); if (pTreeOfOnt!=null && pTreeOfOnt.equals(ontology)) { int minP = Integer.MAX_VALUE, maxP = 0; float avgP = 0; HashMap cache = new HashMap(); if (swoopModel.getFrame().termDisplay.getTrees()[1]!=null) { TreeModel pTreeModel = swoopModel.getFrame().termDisplay.getTrees()[1].getModel(); DefaultMutableTreeNode pRoot = (DefaultMutableTreeNode) pTreeModel.getRoot(); Enumeration depthFirstEnum = pRoot.depthFirstEnumeration(); int numLeaves = 0; while ( depthFirstEnum.hasMoreElements() ) { DefaultMutableTreeNode node = (DefaultMutableTreeNode)depthFirstEnum.nextElement(); if ( node.isLeaf() ) { numLeaves++; TreeNode [] path = node.getPath(); int depth = path.length - 2; // path.length - 2 = number of links from top node if ( depth > 0) this.cacheDepth(cache, String.valueOf(depth), (Set)((DefaultMutableTreeNode)path[1]).getUserObject()); if ( depth == 0 ) this.cacheDepth(cache, String.valueOf(depth), (Set)(pRoot).getUserObject()); if ( depth > maxP ) maxP = depth; if ( depth < minP ) minP = depth; avgP = avgP + depth; } } avgP = avgP / numLeaves; maxPr = String.valueOf(maxP); minPr = String.valueOf(minP); avgPr = String.valueOf(avgP); // get element set corresponding to min/max maxPSet = (HashSet) cache.get(maxPr); minPSet = (HashSet) cache.get(minPr); } } if (avgPr.length()>4) avgPr = avgPr.substring(0, 4); stats.put(this.AVG_DEPTH_PROP_TREE, avgPr); List minPList = new ArrayList(); minPList.add(minPr); minPList.add(minPSet); stats.put(this.MIN_DEPTH_PROP_TREE, minPList); List maxPList = new ArrayList(); maxPList.add(maxPr); maxPList.add(maxPSet); stats.put(this.MAX_DEPTH_PROP_TREE, maxPList); // multiple inheritance for classes Set mult = new HashSet(); if (reasoner.getOntology()!=null && reasoner.getOntology().equals(ontology)) { for (Iterator iter = ontology.getClasses().iterator(); iter.hasNext();) { OWLClass cla = (OWLClass) iter.next(); if (reasoner.isConsistent(cla) && reasoner.superClassesOf(cla).size()>1) mult.add(cla); } } stats.put(this.MULTIPLE_INHERITANCE_CLASS, mult); } catch (Exception ex) { ex.printStackTrace(); } return stats; } /* * cache stores map: depth no. -> set of classes/props with depth */ private void cacheDepth(HashMap cache, String key, Set val) { Set curr = new HashSet(); if (cache.containsKey(key)) curr = (HashSet) cache.get(key); curr.addAll(val); cache.put(key, curr); } private int getDescendentCount(TreeNode node) { int count = 0; while (((DefaultMutableTreeNode) node).getNextNode()!=null) { count ++; node = ((DefaultMutableTreeNode) node).getNextNode(); } return count; } }