diff --git a/src/fr/inrialpes/exmo/align/impl/eval/AveragePRGraphEvaluator.java b/src/fr/inrialpes/exmo/align/impl/eval/AveragePRGraphEvaluator.java new file mode 100644 index 0000000000000000000000000000000000000000..fde5972ee838cc7208de25f728249ad57169374c --- /dev/null +++ b/src/fr/inrialpes/exmo/align/impl/eval/AveragePRGraphEvaluator.java @@ -0,0 +1,231 @@ +/* + * $Id: AveragePRGraphEvaluator.java 1196 2010-01-10 19:58:52Z euzenat $ + * + * Copyright (C) INRIA, 2004-2005, 2007-2010 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +package fr.inrialpes.exmo.align.impl.eval; + +import org.semanticweb.owl.align.Alignment; +import org.semanticweb.owl.align.AlignmentException; +import org.semanticweb.owl.align.Cell; + +import java.util.Enumeration; +import java.util.Properties; +import java.util.Iterator; +import java.util.TreeSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.Comparator; +import java.util.Vector; +import java.io.PrintWriter; +import java.net.URI; + +/** + * Compute the precision recall graph on 11 points + * The first alignment is thus the expected one. + * + * @author Jerome Euzenat + * @version $Id: AveragePRGraphEvaluator.java 1196 2010-01-10 19:58:52Z euzenat $ + * + * The computation is remotely inspired from the sample programme of + * Raymond J. Mooney + * available under GPL from http://www.cs.utexas.edu/users/mooney/ir-course/ + * + * Mooney also provides the averaging of these graphs over several queries: + * unfortunatelly, the resulting graph is not anymore a Precision/Recall graph + * + * This works perfectly correctly. I mention below the point which are + * mentionned as design points in a forecoming Exmotto entry: + * [R=0%] What should be P when R is 0% (obviously 100%) + * [R=100%] What should be P when R=100% is unreachable + * [Interp.] How is a chaotic curve interpolated + * + * Note: a very interesting measure is the MAP (mean average precision) + * which is figuratively the area under the curve and more precisely + * the average precision obtained for each correspondence in the reference + * alignment. + * The problem is that it can only be valid if the compared alignment has + * provided all the correspondences in the reference. + * Otherwise, it would basically be: + * SUM_c\in correct( P( c ) ) / nbexpected + * + * NOTE (JE:2010): This should be adapated for other notions of Precision and Recall + * + */ + +public class AveragePRGraphEvaluator extends GraphEvaluator { + + private int size = 0; // for averaging + + // The eleven values of precision and recall + private double[] precisions = null; + + private double map = 0.0; // For MAP + private double rawmap = 0.0; // For MAP + + public AveragePRGraphEvaluator() { + super(); + precisions = new double[ STEP+1 ]; + points = new Vector<Pair>(); + } + + /** + * Returns the points to display in a graph + */ + public Vector<Pair> eval(){ + Vector<Pair> result = new Vector<Pair>(STEP+1); + // Compute the average and build the vector pair + //System.err.println( " Size: "+size ); + for( int j = 0; j <= STEP; j++ ) { + // JE: better with j/10 + //System.err.println( " prec at "+j+" : "+precisions[j] ); + result.add( new Pair( ((double)j)/10, precisions[j] / size ) ); + } + map = rawmap / size; // average map + return result; + } + + /** + * Compute precision and recall graphs. + */ + public Vector<Pair> eval( Properties params ) { // throws AlignmentException + return eval(); + } + + public void ingest( Alignment al, Alignment reference ) { + size++; + try { + evalAlignment( reference, al ); + } catch ( AlignmentException aex ) { + aex.printStackTrace(); + } + } + + public void evalAlignment( Alignment align1, Alignment align2 ) throws AlignmentException { + // Local variables + int nbexpected = align1.nbCells(); + int nbfound = 0; + int nbcorrect = 0; + double sumprecisions = 0.; // For MAP + Vector<Pair> inflexion = new Vector<Pair>(); + + // Create a sorted structure in which putting the cells + initCellSet(); + if ( align2 == null ) return; //no increase of precisions + for ( Cell c : align2 ) { + cellSet.add( new EvalCell( c, isCorrect( c, align1 ) ) ); + } + + // Collect the points that change recall + // (the other provide lower precision from the same recall and are not considered) + inflexion.add( new Pair( 0., 1. ) ); // [R=0%] + for( EvalCell c2 : cellSet ){ + nbfound++; + if ( c2.correct() ) { //correctCell( c2, align2, align1 ) > 0. + nbcorrect++; + double recall = (double)nbcorrect / (double)nbexpected; + double precision = (double)nbcorrect / (double)nbfound; + sumprecisions += precision; // For MAP + // Create a new pair to put in the list + // It records real precision and recall at that point + inflexion.add( new Pair( recall, precision ) ); + c2 = null; // out of the loop. + } + } + + // Now if we want to have a regular curve we must penalize those system + // that do not reach 100% recall. + // for that purpose, and for each other bound we add a point with the worse + // precision which is the required recall level divided with the maximum + // cardinality possible (i.e., the multiplication of the ontology sizes). + // JE[R=100%]: that's a fine idea! Unfortunately SIZEOFO1 and SIZEOFO2 are undefined values + //inflexion.add( new Pair( 1., (double)nbexpected/(double)(SIZEOFO1*SIZEOFA2) ) ); + inflexion.add( new Pair( 1.0, 0. ) ); // useless because + + // [Interp.] Interpolate curve points at each n-recall level + // This is inspired form Ray Mooney's program + // It works backward in the vector, + // (in the same spirit as before, the maximum value so far -best- is retained) + int j = inflexion.size()-1; // index in recall-ordered vector of points + //System.err.println( "Inflexion: "+j); + int i = STEP; // index of the current recall interval + double level = (double)i/STEP; // max level of that interval + double best = 0.; // best value found for that interval + while( j >= 0 ){ + Pair precrec = inflexion.get(j); + while ( precrec.getX() < level ){ + precisions[i] += best; //?? + i--; + level = (double)i/STEP; + }; + if ( precrec.getY() > best ) best = precrec.getY(); + j--; + } + precisions[0] += best; // It should be 1. that's why it is now added in points. [R=0%] + + rawmap += sumprecisions / nbexpected; // For MAP (average MAP) + } + + /** + * This output the result + */ + public void write(PrintWriter writer) throws java.io.IOException { + writer.println("<?xml version='1.0' encoding='utf-8' standalone='yes'?>"); + writer.println("<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>"); + writer.println(" <output rdf:about=''>"); + for( int i=0; i <= STEP; i++ ){ + writer.print(" <step>\n <recall>"); + writer.print((double)i/STEP); + writer.print("</recall>\n <precision>"); + writer.print(precisions[i]); + writer.print("</precision>\n </step>\n"); + } + writer.print(" <MAP>"+map+"</MAP>\n"); + writer.print(" </output>\n</rdf:RDF>\n"); + } + + /* Write out the final interpolated recall/precision graph data. + * One line for each recall/precision point in the form: 'R-value P-value'. + * This is the format needed for GNUPLOT. + */ + public void writePlot( PrintWriter writer ) { + for(int i = 0; i < STEP+1; i++){ + writer.println( (double)i/10 + "\t" + precisions[i]); + } + } + + public double getPrecision( int i ){ + return precisions[i]; + } + + /* + * Only valid once eval() has been called + */ + public double getMAP(){ + return map; + } + + /** + * Retuns a simple global evaluation measure if any + */ + public double getGlobalResult(){ + return map; + } +} + diff --git a/src/fr/inrialpes/exmo/align/impl/eval/GraphEvaluator.java b/src/fr/inrialpes/exmo/align/impl/eval/GraphEvaluator.java index 5fc6ae2c3276ea497de6d41abbacb7dfc3648d2e..f2611e2fa7c123227bf635ad0f23f6f27ee511bb 100644 --- a/src/fr/inrialpes/exmo/align/impl/eval/GraphEvaluator.java +++ b/src/fr/inrialpes/exmo/align/impl/eval/GraphEvaluator.java @@ -32,9 +32,9 @@ import java.util.Properties; import java.util.Iterator; import java.util.TreeSet; import java.util.Set; +import java.util.Vector; import java.util.SortedSet; import java.util.Comparator; -import java.util.Vector; import java.io.PrintWriter; import java.net.URI; @@ -43,104 +43,139 @@ import java.net.URI; * instead of values (or sets of values) * Pair: only used for recording sets of points in a curve * + * GraphEvaluator is used (generically) in the following way: + * - create a GraphEvaluator (new GraphEvaluator) + * - fill it with the set of results that you want to evaluate + * (.ingest( Alignment, Alignment) and this repetively + * - Finally create plot (.eval() ) + * + * This abstract class provides the ingest method but not eval which has to be + * implemented in subclasses. ingest can be rewritten as well. + * * @author Jerome Euzenat * @version $Id$ */ -public abstract class GraphEvaluator extends BasicEvaluator { +public abstract class GraphEvaluator { + + /** + * The resolution of the provided result: by STEP steps + */ + protected int STEP = 10; - public Vector<Pair> points = null; + protected int nbexpected = 0; + protected SortedSet<EvalCell> cellSet = null; + public Vector<Pair> points; + + /** + * Returns the points to display in a graph + */ + public abstract Vector<Pair> eval() throws AlignmentException; + /** + * Returns the points to display in a graph + */ + public abstract Vector<Pair> eval( Properties params ) throws AlignmentException; + /** + * Retuns a simple global evaluation measure if any + */ + public abstract double getGlobalResult(); + + public void setStep( int i ) { + if ( 0 < i && i <= 100 ) STEP = i; + } + public int getStep() { return STEP; } /** Creation: * A priori, evaluators can deal with any kind of alignments. * However, it will not work if these are not of the same type. **/ - public GraphEvaluator( Alignment align1, Alignment align2 ) { - super(align1, align2); - if ( align1.getClass() != align2.getClass() ) { - // This should throw an exception... - } - points = new Vector<Pair>(); - } - - /** - * Compute precision and recall graphs. - * The algorithm is as follows: - * 1) Order the pairs of the found alignment. - * 2) For - */ - public double eval( Properties params ) throws AlignmentException { - return eval( params, (Object)null ); + public GraphEvaluator() { + initCellSet(); } - public SortedSet<Cell> orderAlignment() { + protected void initCellSet () { // Create a sorted structure in which putting the cells // TreeSet could be replaced by something else - SortedSet<Cell> cellSet = new TreeSet<Cell>( - new Comparator<Cell>() { - public int compare( Cell o1, Cell o2 ) + cellSet = new TreeSet<EvalCell>( + new Comparator<EvalCell>() { + public int compare( EvalCell o1, EvalCell o2 ) throws ClassCastException { - try { + //try { //System.err.println(((Cell)o1).getObject1()+" -- "+((Cell)o1).getObject2()+" // "+o2.getObject1()+" -- "+o2.getObject2()); - if ( o1 instanceof Cell && o2 instanceof Cell ) { - if ( o1.getStrength() > o2.getStrength() ){ + if ( o1.cell instanceof Cell && o2.cell instanceof Cell ) { + if ( o1.cell.getStrength() > o2.cell.getStrength() ){ return -1; - } else if ( o1.getStrength() < o2.getStrength() ){ + } else if ( o1.cell.getStrength() < o2.cell.getStrength() ){ return 1; //The comparator must always tell that things are different! - } else if ( (o1.getObject1AsURI(align1).getFragment() == null) - || (o2.getObject1AsURI(align2).getFragment() == null) ) { + } else if ( o1.correct ) { + return -1; + } + /*else if ( (o1.cell.getObject1AsURI(align1).getFragment() == null) + || (o2.cell.getObject1AsURI(align2).getFragment() == null) ) { return -1; - } else if ( o1.getObject1AsURI(align1).getFragment().compareTo(o2.getObject1AsURI(align2).getFragment()) > 0) { + } else if ( o1.cell.getObject1AsURI(align1).getFragment().compareTo(o2.cell.getObject1AsURI(align2).getFragment()) > 0) { return -1; - } else if ( o1.getObject1AsURI(align1).getFragment().compareTo(o2.getObject1AsURI(align2).getFragment()) < 0 ) { + } else if ( o1.cell.getObject1AsURI(align1).getFragment().compareTo(o2.cell.getObject1AsURI(align2).getFragment()) < 0 ) { return 1; - } else if ( (o1.getObject2AsURI(align1).getFragment() == null) - || (o2.getObject2AsURI(align2).getFragment() == null) ) { + } else if ( (o1.cell.getObject2AsURI(align1).getFragment() == null) + || (o2.cell.getObject2AsURI(align2).getFragment() == null) ) { return -1; - } else if ( o1.getObject2AsURI(align1).getFragment().compareTo(o2.getObject2AsURI(align2).getFragment()) > 0) { + } else if ( o1.cell.getObject2AsURI(align1).getFragment().compareTo(o2.cell.getObject2AsURI(align2).getFragment()) > 0) { return -1; // We assume that they have different names - } else { return 1; } + } */ else { return 1; } } else { throw new ClassCastException(); } - } catch ( AlignmentException e) { e.printStackTrace(); return 0;} + //} catch ( AlignmentException e) { e.printStackTrace(); return 0;} } } ); + } + /* + * Tells if the cell is found in the reference alignment + * (without relation consideration) + */ + public void ingest( Alignment al, Alignment ref ){ + nbexpected += ref.nbCells(); // Set the found cells in the sorted structure - for ( Cell c : align2 ) { - cellSet.add( c ); + if ( al == null ) return; + for ( Cell c : al ) { + cellSet.add( new EvalCell( c, isCorrect( c, ref ) ) ); } + } - return cellSet; + public int nbCells() { + if ( cellSet == null ) return 0; + else return cellSet.size(); } /* - * This checks if a particular cell is in the reference alignment or not. - * This could be changed for other kind of correctness (e.g., Semantics). + * Tells if the cell is found in the reference alignment + * (without relation consideration) */ - public double correctCell( Cell c2, Alignment align2, Alignment refalign ) throws AlignmentException { - Set s1 = (Set)refalign.getAlignCells1( c2.getObject1() ); - if( s1 != null ) { // for all cells matching our first entity - for( Iterator it1 = s1.iterator(); it1.hasNext(); ){ - Cell c1 = (Cell)it1.next(); - URI uri1 = c1.getObject2AsURI(refalign); - URI uri2 = c2.getObject2AsURI(align2); - if (uri1.toString().equals(uri2.toString())) { //This cell matches a correct one - return 1.; + public boolean isCorrect( Cell c, Alignment ref ) { + try { + Set<Cell> s2 = ref.getAlignCells1( c.getObject1() ); + if( s2 == null ) return false; + URI uri1 = c.getObject2AsURI(); + for( Cell c2 : s2 ){ + URI uri2 = c2.getObject2AsURI(); + if (uri1.toString().equals(uri2.toString())) { + return true; } } + } catch ( AlignmentException aex ) { + aex.printStackTrace(); } - return 0.; + return false; } /** - * This output the result + * This output the resulting plot in XML */ - public void writeXMLMap(PrintWriter writer) throws java.io.IOException { - for( int j = 0; j < points.size(); j++ ){ - Pair precrec = points.get(j); + public void writeXMLMap( PrintWriter writer) throws java.io.IOException { + for( Pair precrec: points ) { writer.print(" <step>\n <x>"); writer.print( precrec.getX() ); writer.print("</x>\n <y>"); @@ -149,44 +184,27 @@ public abstract class GraphEvaluator extends BasicEvaluator { } } - /** - * This output the result - */ - public void writeFullPlot(PrintWriter writer) { - for( int j = 0; j < points.size(); j++ ){ - Pair precrec = points.get(j); - writer.println( precrec.getX()+" "+precrec.getY() ); - } - } - /* Write out the final interpolated recall/precision graph data. * One line for each recall/precision point in the form: 'R-value P-value'. * This is the format needed for GNUPLOT. - public void writePlot( PrintWriter writer ) throws java.io.IOException { - for(int i = 0; i < STEP+1; i++){ - writer.println( (double)i/10 + "\t" + precisions[i]); - } - } */ - public void writePlot( PrintWriter writer ) { - // Print header - int size = points.size(); - writer.println("#Curve 0, "+size+" points"); - writer.println("#x y type"); - writer.println("%% Plot generated by GraphEvaluator of alignapi"); - writer.println("%% Include in PGF tex by:\n"); - writer.println("%% \\begin{tikzpicture}[cap=round]"); - writer.println("%% \\draw[step="+size+"cm,very thin,color=gray] (-0.2,-0.2) grid ("+size+","+size+");"); - writer.println("%% \\draw[->] (-0.2,0) -- (10.2,0) node[right] {$recall$}; "); - writer.println("%% \\draw[->] (0,-0.2) -- (0,10.2) node[above] {$precision$}; "); - //writer.println("%% \\draw plot[mark=+,smooth] file {"+algo+".table};"); - writer.println("%% \\end{tikzpicture}"); - writer.println(); - for( int j = 0; j < size; j++ ){ - Pair precrec = points.get(j); - writer.println( precrec.getX()+" "+precrec.getY() ); + for( Pair p : points ){ + writer.println( p.getX()/10 + "\t" + p.getY() ); } } } + +class EvalCell { + Cell cell = null; + boolean correct = false; + + public EvalCell( Cell c, boolean b ){ + cell = c; + correct = b; + } + + public boolean correct() { return correct; } + public Cell cell() { return cell; } +} diff --git a/src/fr/inrialpes/exmo/align/impl/eval/PRGraphEvaluator.java b/src/fr/inrialpes/exmo/align/impl/eval/PRGraphEvaluator.java index 646cfbcf5ce9444c5f592f2c0aab68ab56d413f5..f37423e3df7c9dd0f16baf84774781c212fbcea7 100644 --- a/src/fr/inrialpes/exmo/align/impl/eval/PRGraphEvaluator.java +++ b/src/fr/inrialpes/exmo/align/impl/eval/PRGraphEvaluator.java @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (C) INRIA, 2004-2005, 2007-2009 + * Copyright (C) INRIA, 2004-2005, 2007-2010 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -41,7 +41,6 @@ import java.net.URI; /** * Compute the precision recall graph on 11 points - * The first alignment is thus the expected one. * * @author Jerome Euzenat * @version $Id$ @@ -69,6 +68,7 @@ import java.net.URI; * SUM_c\in correct( P( c ) ) / nbexpected * * NOTE (JE:2010): This should be adapated for other notions of Precision and Recall + * This is fully possible and will be done with * */ @@ -76,18 +76,150 @@ public class PRGraphEvaluator extends GraphEvaluator { private int STEP = 10; - // The eleven values of precision and recall - private double[] precisions = null; - private double map = 0.0; // For MAP + private double sumprecisions = 0.; // For MAP + + public PRGraphEvaluator() { + super(); + } - public PRGraphEvaluator(Alignment align1, Alignment align2) { - super(align1, align2); + /** + * Compute precision and recall graphs. + */ + public Vector<Pair> eval() { // throws AlignmentException + return eval( (Properties)null ); } /** * Compute precision and recall graphs. */ + public Vector<Pair> eval( Properties params ) { // throws AlignmentException + points = new Vector<Pair>(STEP+1); // 2010evaluate the size + + if ( params != null && params.getProperty( "oldstyle" ) != null ) { + return evalOldStyle(); + } else { + return evalOpenEnded(); + } + } + + /** + * Returns a list of Precision at recall points (Pairs) + * From an ordered vector of cells with their correctness status + */ + public Vector<Pair> evalOpenEnded() { + double[] precisions = new double[STEP+1]; + // Determine what the increment is + // Get the increment + int nbcorrect = 0; + int nbfound = 0; + int increment = (STEP*nbexpected)/100; // 2010 should be computed with the total expected (in negrapheval) + System.err.println(" INCREMENT SET "+increment ); + int next = 0; + points.add( new Pair( 0., 1. ) ); // [R=0%] + next += increment; + for ( EvalCell c : cellSet ) { + nbfound++; + if ( c.correct() ) { + nbcorrect++; + double precision = (double)nbcorrect / (double)nbfound; + sumprecisions += precision; // For MAP + if ( nbcorrect == next ) { // increrement achieved + //record value + double recall = (double)nbcorrect / (double)nbexpected; + // Here the recall could be computed more directly + points.add( new Pair( recall, precision ) ); + next += increment; + } + } + } + // In the end, the last point on the curve should be the global precision? + // No, in the end, it should be the real-final-precision and recall, i.e., + points.add( new Pair( (double)nbcorrect / (double)nbexpected, (double)nbcorrect / (double)nbfound ) ); + + // [Interp.] No interpolation is made there... + + // MAP is provided with regard to the expected number of inflexion points + map = sumprecisions / nbexpected; + + return points; + } + + /** + * Returns a list of Precision at recall points (Pairs) + * From an ordered vector of cells with their correctness status + */ + public Vector<Pair> evalOldStyle() { + double[] precisions = new double[STEP+1]; + // Determine what the increment is + // Get the increment + int nbcorrect = 0; + int nbfound = 0; + int increment = (STEP*nbexpected)/100; // 2010 should be computed with the total expected (in negrapheval) + Vector<Pair> inflexion = new Vector<Pair>(); + System.err.println(" INCREMENT SET "+increment ); + int next = 0; + // Collect the points that change recall + // (the other provide lower precision from the same recall and are not considered) + inflexion.add( new Pair( 0., 1. ) ); // [R=0%] + for( EvalCell c2 : cellSet ){ + nbfound++; + if ( c2.correct() ) { //correctCell( c2, align2, align1 ) > 0. + nbcorrect++; + double recall = (double)nbcorrect / (double)nbexpected; + double precision = (double)nbcorrect / (double)nbfound; + sumprecisions += precision; // For MAP + // Create a new pair to put in the list + // It records real precision and recall at that point + inflexion.add( new Pair( recall, precision ) ); + c2 = null; // out of the loop. + } + } + + // Now if we want to have a regular curve we must penalize those system + // that do not reach 100% recall. + // for that purpose, and for each other bound we add a point with the worse + // precision which is the required recall level divided with the maximum + // cardinality possible (i.e., the multiplication of the ontology sizes). + // JE[R=100%]: that's a fine idea! Unfortunately SIZEOFO1 and SIZEOFO2 are undefined values + //inflexion.add( new Pair( 1., (double)nbexpected/(double)(SIZEOFO1*SIZEOFA2) ) ); + inflexion.add( new Pair( 1.0, 0. ) ); // useless because + + // [Interp.] Interpolate curve points at each n-recall level + // This is inspired form Ray Mooney's program + // It works backward in the vector, + // (in the same spirit as before, the maximum value so far -best- is retained) + int j = inflexion.size()-1; // index in recall-ordered vector of points + //System.err.println( "Inflexion: "+j); + int i = STEP; // index of the current recall interval + double level = (double)i/STEP; // max level of that interval + double best = 0.; // best value found for that interval + while( j >= 0 ){ + Pair precrec = inflexion.get(j); + while ( precrec.getX() < level ){ + precisions[i] = best; //?? + i--; + level = (double)i/STEP; + }; + if ( precrec.getY() > best ) best = precrec.getY(); + j--; + } + precisions[0] = best; // It should be 1. that's why it is now added in points. [R=0%] + + for( i = 0; i <= STEP; i++ ) { + // JE: better with j/10 + //System.err.println( " prec at "+j+" : "+precisions[j] ); + points.add( new Pair( ((double)i)/10, precisions[i] ) ); + } + + // MAP is provided with regard to the expected number of inflexion points + map = sumprecisions / nbexpected; + + return points; + } + + /** + * Compute precision and recall graphs. public double eval( Properties params ) throws AlignmentException { return eval( params, (Object)null ); } @@ -102,7 +234,7 @@ public class PRGraphEvaluator extends GraphEvaluator { if( params.getProperty("step") != null ){ STEP = Integer.parseInt( params.getProperty("step") ); } - precisions = new double[ STEP+1 ]; + points = new double[ STEP+1 ]; // Create a sorted structure in which putting the cells // TreeSet could be replaced by something else @@ -145,18 +277,19 @@ public class PRGraphEvaluator extends GraphEvaluator { while( j >= 0 ){ Pair precrec = points.get(j); while ( precrec.getX() < level ){ - precisions[i] = best; + points[i] = best; i--; level = (double)i/STEP; }; if ( precrec.getY() > best ) best = precrec.getY(); j--; } - precisions[0] = best; // It should be 1. that's why it is now added in points. [R=0%] + points[0] = best; // It should be 1. that's why it is now added in points. [R=0%] map = sumprecisions / nbexpected; // For MAP return map; } + */ /** * This output the result @@ -165,32 +298,15 @@ public class PRGraphEvaluator extends GraphEvaluator { writer.println("<?xml version='1.0' encoding='utf-8' standalone='yes'?>"); writer.println("<"+SyntaxElement.RDF.print()+" xmlns:"+Namespace.RDF.shortCut+"='"+Namespace.RDF.prefix+"'>"); writer.println(" <output "+SyntaxElement.RDF_ABOUT.print()+"=''>"); - for( int i=0; i <= STEP; i++ ){ - writer.print(" <step>\n <recall>"); - writer.print((double)i/STEP); - writer.print("</recall>\n <precision>"); - writer.print(precisions[i]); - writer.print("</precision>\n </step>\n"); - } + writeXMLMap( writer ); writer.print(" <MAP>"+map+"</MAP>\n"); writer.print(" </output>\n</"+SyntaxElement.RDF.print()+">\n"); } - /* Write out the final interpolated recall/precision graph data. - * One line for each recall/precision point in the form: 'R-value P-value'. - * This is the format needed for GNUPLOT. - */ - public void writePlot( PrintWriter writer ) { - for(int i = 0; i < STEP+1; i++){ - writer.println( (double)i/10 + "\t" + precisions[i]); - } - } - - public double getPrecision( int i ){ - return precisions[i]; + public double getMap(){ + return map; } - - public double getMAP(){ + public double getGlobalResult(){ return map; } } diff --git a/src/fr/inrialpes/exmo/align/impl/eval/Pair.java b/src/fr/inrialpes/exmo/align/impl/eval/Pair.java index fc2da3157c837913f0ea695fc196d5bff4ae1242..d8bebab883c0bbfbc80e71040afc03a50e0d960e 100644 --- a/src/fr/inrialpes/exmo/align/impl/eval/Pair.java +++ b/src/fr/inrialpes/exmo/align/impl/eval/Pair.java @@ -30,4 +30,6 @@ public class Pair { } public double getX(){ return x; } public double getY(){ return y; } + public void setX( double d ){ x = d; } + public void setY( double d ){ y = d; } } diff --git a/src/fr/inrialpes/exmo/align/impl/eval/ROCCurveEvaluator.java b/src/fr/inrialpes/exmo/align/impl/eval/ROCCurveEvaluator.java index aacf667ac4cfb9e4c9726b8725e69a88cbd0b6f8..671d96e7e9eea1311075fe7e6cd12802718cc8d1 100644 --- a/src/fr/inrialpes/exmo/align/impl/eval/ROCCurveEvaluator.java +++ b/src/fr/inrialpes/exmo/align/impl/eval/ROCCurveEvaluator.java @@ -41,7 +41,6 @@ import java.net.URI; /** * Compute ROCCurves - * The first alignment is thus the expected one * * @author Jerome Euzenat * @version $Id$ @@ -80,45 +79,82 @@ public class ROCCurveEvaluator extends GraphEvaluator { private double auc = 0.0; - public ROCCurveEvaluator( Alignment align1, Alignment align2 ) { - super(align1, align2); + public ROCCurveEvaluator() { + super(); } /** - * Compute ROCCurve + * Compute ROCCurve points + * From an ordered vector of cells with their correctness status */ - public double eval( Properties params ) throws AlignmentException { - return eval( params, (Object)null ); - } - public double eval( Properties params, Object cache ) throws AlignmentException { + public Vector<Pair> eval( Properties param ) { // Local variables int nbfound = 0; int area = 0; int x = 0; int y = 0; - int scale = align2.nbCells(); + //int scale = align2.nbCells(); + int scale = 0; + if ( param != null && param.getProperty( "scale" ) != null ) { + scale = Integer.parseInt( param.getProperty( "scale" ) ); + } points = new Vector<Pair>(); - // Create a sorted structure in which putting the cells - // TreeSet could be replaced by something else - SortedSet<Cell> cellSet = orderAlignment(); - // Collect the points in the curve - points.add( new Pair( 0., 0. ) ); // [Origin] - for( Cell c2 : cellSet ) { + Pair last = new Pair( 0., 0. ); // [Origin] + for( EvalCell c : cellSet ) { nbfound++; - if ( correctCell( c2, align2, align1 ) > 0. ) { + if ( c.correct() ) { y++; + if ( last.getX() != x ) { + points.add( last ); + last = new Pair( x, y-1 ); + } } else { x++; area += y; + if ( last.getY() != y ) { + points.add( last ); + last = new Pair( x-1, y ); + } } - points.add( new Pair( x, y ) ); } - auc = (double)area / (double)nbfound; + points.add( last ); + points.add( new Pair( x, y ) ); - return auc; + /* + * This is not ideal because the measure is given curve by curve + * only as far as the curves continues. Adding the Max between all curves + * would be better + */ + if ( nbfound != 0 ) { // or x != 0 + //auc = (double)area / (double)(nbexpected * x ); + auc = (double)(area + y*y ) / (double)(nbexpected * nbfound ); + if ( scale != 0 ){ + auc = (double)(area + y*(scale-x) ) / (double)(nbexpected * scale ); + } + } else { + auc = 0.00; + } + + // Scale + for ( Pair p : points ){ + if ( scale != 0 ){ + p.setX( (double)(p.getX()) / scale ); + } else if ( x != 0 ) p.setX( (double)(p.getX()) / x ); + p.setY( (double)(p.getY()) / nbexpected ); + } + //points.add( new Pair( 1., y/nbexpected ) ); + + return points; + } + + /** + * For the moment + */ + public Vector<Pair> eval() { + return eval( (Properties)null ); } /** @@ -134,9 +170,18 @@ public class ROCCurveEvaluator extends GraphEvaluator { } public void writePlot(PrintWriter writer) { - writeFullPlot( writer ); + for( Pair p : points ){ + writer.println( p.getX()/10 + "\t" + p.getY() ); + } } + public double getPlotResult( int i ){ + return 0.0; + } + + public double getGlobalResult(){ + return auc; + } public double getAUC(){ return auc; } diff --git a/src/fr/inrialpes/exmo/align/util/GenPlot.java b/src/fr/inrialpes/exmo/align/util/GenPlot.java index 2391f9c09dc1c490a22f407a5a4884740138dc32..f4e47afb510c21a4d79e01ee5d69882dbc91a7e8 100644 --- a/src/fr/inrialpes/exmo/align/util/GenPlot.java +++ b/src/fr/inrialpes/exmo/align/util/GenPlot.java @@ -28,6 +28,7 @@ import org.semanticweb.owl.align.Alignment; import fr.inrialpes.exmo.align.impl.eval.GraphEvaluator; import fr.inrialpes.exmo.align.impl.eval.PRGraphEvaluator; +import fr.inrialpes.exmo.align.impl.eval.Pair; import fr.inrialpes.exmo.ontowrap.OntologyFactory; @@ -43,7 +44,8 @@ import java.util.Hashtable; import java.util.Properties; import java.util.Vector; import java.util.Enumeration; -import java.util.StringTokenizer; +import java.lang.reflect.Constructor; +import java.lang.InstantiationException; import org.xml.sax.SAXException; @@ -52,7 +54,8 @@ import gnu.getopt.Getopt; import fr.inrialpes.exmo.align.parser.AlignmentParser; -/** A basic class for synthesizing the alignment results of an algorithm by a +/** + * A basic class for synthesizing the alignment results of an algorithm by a * precision recall graph. * * These graphs are however computed on averaging the precision recall/graphs @@ -70,6 +73,8 @@ import fr.inrialpes.exmo.align.parser.AlignmentParser; * -d debug --debug=level * -l list of compared algorithms * -t output --type=output: xml/tex/html/ascii + * -e classname --evaluator=classname + * -g classname --grapher=classname * </pre> * * The input is taken in the current directory in a set of subdirectories (one per @@ -92,11 +97,16 @@ public class GenPlot { int STEP = 10; Properties params = null; Vector<String> listAlgo; + Vector<GraphEvaluator> listEvaluators; String fileNames = ""; String outFile = null; - java.lang.reflect.Constructor evalConstructor = null; + Constructor evalConstructor = null; + Constructor graphConstructor = null; + String ylabel = "precision"; + String xlabel = "recall"; String type = "tsv"; int debug = 0; + int size = 0; // the set of algo to compare PrintWriter output = null; public static void main(String[] args) { @@ -105,19 +115,23 @@ public class GenPlot { } public void run(String[] args) throws Exception { - LongOpt[] longopts = new LongOpt[8]; + LongOpt[] longopts = new LongOpt[10]; longopts[0] = new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'); longopts[1] = new LongOpt("output", LongOpt.REQUIRED_ARGUMENT, null, 'o'); longopts[3] = new LongOpt("type", LongOpt.REQUIRED_ARGUMENT, null, 't'); longopts[4] = new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'd'); longopts[5] = new LongOpt("evaluator", LongOpt.REQUIRED_ARGUMENT, null, 'e'); - longopts[6] = new LongOpt("list", LongOpt.REQUIRED_ARGUMENT, null, 'l'); + longopts[6] = new LongOpt("grapher", LongOpt.REQUIRED_ARGUMENT, null, 'g'); + longopts[7] = new LongOpt("list", LongOpt.REQUIRED_ARGUMENT, null, 'l'); + longopts[8] = new LongOpt("step", LongOpt.REQUIRED_ARGUMENT, null, 's'); - Getopt g = new Getopt("", args, "ho:d::l:e:t:", longopts); + Getopt g = new Getopt("", args, "ho:d::l:e:g:s:t:", longopts); + int step = 10; int c; String arg; - String cl = "fr.inrialpes.exmo.align.impl.eval.PRGraphEvaluator"; + String evalCN = "fr.inrialpes.exmo.align.impl.eval.PRecEvaluator"; + String graphCN = "fr.inrialpes.exmo.align.impl.eval.PRGraphEvaluator"; while ((c = g.getopt()) != -1) { switch (c) { @@ -129,8 +143,16 @@ public class GenPlot { outFile = g.getOptarg(); break; case 'e' : - /* Name of the class to compute */ - cl = g.getOptarg(); + /* Name of the evaluator to use */ + evalCN = g.getOptarg(); + break; + case 'g' : + /* Name of the graph display to use */ + graphCN = g.getOptarg(); + if ( graphCN.equals("fr.inrialpes.exmo.align.impl.eval.ROCCurveEvaluator") ) { + xlabel = "noise"; + ylabel = "recall"; + } break; case 't' : /* Type of output (tex/tsv(/html/xml/ascii)) */ @@ -140,6 +162,10 @@ public class GenPlot { /* List of filename */ fileNames = g.getOptarg(); break; + case 's' : + /* Step */ + fileNames = g.getOptarg(); + break; case 'd' : /* Debug level */ arg = g.getOptarg(); @@ -149,22 +175,44 @@ public class GenPlot { } } - Class<?> evalClass = Class.forName( cl ); - Class<?> oClass = Class.forName("org.semanticweb.owl.align.Alignment"); - Class[] cparams = { oClass, oClass }; - evalConstructor = evalClass.getConstructor( cparams ); + Class<?> graphClass = Class.forName(graphCN); + Class[] cparams = {}; + graphConstructor = graphClass.getConstructor( cparams ); + + //Class<?> evalClass = Class.forName(evalCN); + //evalConstructor = evalClass.getConstructor( cparams ); - // JE: StringTokenizer is obsoleted in Java 1.4 in favor of split: to change listAlgo = new Vector<String>(); - StringTokenizer st = new StringTokenizer(fileNames,","); - while (st.hasMoreTokens()) { - listAlgo.add(st.nextToken()); + for ( String s : fileNames.split(",") ) { + size++; + listAlgo.add( s ); } params = new Properties(); if (debug > 0) params.setProperty( "debug", Integer.toString( debug-1 ) ); - params.setProperty("step", Integer.toString( STEP ) ); + // Collect correspondences from alignments in all directories + // . -> Vector<EvalCell> + listEvaluators = iterateDirectories(); + + // + int max = 0; + for( GraphEvaluator e : listEvaluators ) { + int n = e.nbCells(); + if ( n > max ) max = n; + } + params.setProperty( "scale", Integer.toString( max ) ); + + // Vector<EvalCell> -> Vector<Pair> + // Convert the set of alignments into the list of required point pairs + // We must convert the + Vector<Vector<Pair>> toplot = new Vector<Vector<Pair>>(); + for( int i = 0; i < size ; i++ ) { + // Convert it with the adequate GraphPlotter + // Scale the point pairs to the current display (local) + toplot.add( i, listEvaluators.get(i).eval( params ) ); + //scaleResults( STEP, + } // Set output file OutputStream stream; @@ -177,11 +225,13 @@ public class GenPlot { new BufferedWriter( new OutputStreamWriter( stream, "UTF-8" )), true); - // type + //System.err.println ( toplot.get(0)); + // Display the required type of output + // Vector<Pair> -> . if ( type.equals("tsv") ){ - printTSV( iterateDirectories() ); + printTSV( toplot ); } else if ( type.equals("tex") ) { - printPGFTex( iterateDirectories() ); + printPGFTex( toplot ); } else System.err.println("Flag -t "+type+" : not implemented yet"); } @@ -191,7 +241,20 @@ public class GenPlot { * The points are computed by aggregating the values * (and in the end computing the average) */ - public double[][] iterateDirectories (){ + public Vector<GraphEvaluator> iterateDirectories() { + Vector<GraphEvaluator> evaluators = new Vector<GraphEvaluator>( size ); + Object[] mparams = {}; + try { + for( int i = 0; i < size; i++ ) { + GraphEvaluator ev = (GraphEvaluator)graphConstructor.newInstance(mparams); + ev.setStep( STEP ); + evaluators.add( i, ev ); + } + } catch (Exception ex) { //InstantiationException, IllegalAccessException + ex.printStackTrace(); + System.exit(-1); + } + File [] subdir = null; try { subdir = (new File(System.getProperty("user.dir"))).listFiles(); @@ -200,100 +263,95 @@ public class GenPlot { usage(); } - // Initialize the vector of results - double[][] result = new double[listAlgo.size()][STEP+1]; - for( int i=0; i < listAlgo.size(); i++){ - for( int j=0; j <= STEP; j++){ - result[i][j] = 0.0; - } - } - - int size = 0; // Evaluate the results in each directory for ( int k = subdir.length-1 ; k >= 0; k-- ) { if( subdir[k].isDirectory() ) { // eval the alignments in a subdirectory - iterateAlignments( subdir[k], result ); - size++; - } - } - - // Compute the average by dividing each value by the number of tests - // (not a very good method anyway) - for( int i=0; i < listAlgo.size(); i++){ - for( int j=0; j <= STEP; j++){ - result[i][j] = result[i][j] / size; + iterateAlignments( subdir[k], evaluators );//, result ); } } - - return result; + return evaluators; } - public void iterateAlignments ( File dir, double[][] result ) { + public void iterateAlignments ( File dir, Vector<GraphEvaluator> evaluators ) { + if ( debug > 0 ) System.err.println("Directory : "+dir); String prefix = dir.toURI().toString()+"/"; - int i = 0; - if( debug > 0 ) System.err.println("Directory : "+dir); + int nextdebug; + if ( debug < 2 ) nextdebug = 0; + else nextdebug = debug - 2; + AlignmentParser aparser = new AlignmentParser( nextdebug ); + Alignment refalign = null; + + try { // Load the reference alignment... + refalign = aparser.parse( prefix+"refalign.rdf" ); + if ( debug > 1 ) System.err.println(" Reference alignment parsed"); + } catch ( Exception aex ) { + if ( debug > 1 ) { + aex.printStackTrace(); + } else { + System.err.println("GenPlot cannot parse refalign : "+aex); + }; + return; + } + // for all alignments there, - for ( String algo : listAlgo ) { - // call eval + for( int i = 0; i < size; i++ ) { + String algo = listAlgo.get(i); + Alignment al = null; if ( debug > 0 ) System.err.println(" Considering result "+algo+" ("+i+")"); - // JE2010GRAPH - PRGraphEvaluator evaluator = eval( prefix+"refalign.rdf", prefix+algo+".rdf"); - // store the result - // This cannot be done for ROC curves - // JE2010GRAPH - if ( evaluator != null ){ - for( int j = 0; j <= STEP ; j++ ){ - result[i][j] += evaluator.getPrecision(j); - } + try { + aparser.initAlignment( null ); + al = aparser.parse( prefix+algo+".rdf" ); + if ( debug > 1 ) System.err.println(" Alignment "+algo+" parsed"); + } catch (Exception ex) { + if ( debug > 1 ) { + ex.printStackTrace(); + } else { + System.err.println("GenPlot: "+ex); + }; } - i++; + // even if empty, declare refalign + evaluators.get(i).ingest( al, refalign ); } // Unload the ontologies. OntologyFactory.clear(); } - - public PRGraphEvaluator eval( String alignName1, String alignName2 ) { - PRGraphEvaluator eval = null; - try { - int nextdebug; - if ( debug < 2 ) nextdebug = 0; - else nextdebug = debug - 2; - // Load alignments - AlignmentParser aparser = new AlignmentParser( nextdebug ); - Alignment align1 = aparser.parse( alignName1 ); - if ( debug > 1 ) System.err.println(" Alignment structure1 parsed"); - aparser.initAlignment( null ); - Alignment align2 = aparser.parse( alignName2 ); - if ( debug > 1 ) System.err.println(" Alignment structure2 parsed"); - // Create evaluator object - // JE2010GRAPH - Object[] mparams = { align1, align2 }; - eval = (PRGraphEvaluator)evalConstructor.newInstance( mparams ); - //eval = new PRGraphEvaluator( align1, align2 ); - // Compare - params.setProperty( "debug", Integer.toString( nextdebug ) ); - eval.eval( params ) ; - // Unload the ontologies. - //loaded.clear(); - } catch (Exception ex) { - if ( debug > 1 ) { - ex.printStackTrace(); - } else { - System.err.println("GenPlot: "+ex); - System.err.println(alignName1+ " - "+alignName2 ); - }; - }; - return eval; + + // should be OK for changing granularity + // This is not really scalling... + // This is unused + public Vector<Pair> scaleResults( int STEP, Vector<Pair> input ) { + int j = 0; + Vector<Pair> output = new Vector<Pair>(); // Set the size! + Pair last = null; + double next = 0.;//is it a double?? + for ( Pair npair : input ) { + if ( npair.getX() == next ) { + output.add( npair ); + next += STEP; + } else if ( npair.getX() >= next ) { // interpolate + double val; + if ( last.getY() >= npair.getY() ) { + val = npair.getY() + ( ( last.getY() - npair.getY() ) / ( last.getX()-npair.getX() ) ); + } else { + val = last.getY() + ( (npair.getY() - last.getY() ) / ( last.getX()-npair.getX() ) ); + } + //System.err.println( "Scaling: "+next+" / "+val ); + output.add( new Pair( next, val ) ); + next += STEP; + } + last = npair; + } + output.add( last ); + return( output ); } - /** * This does average plus plot * */ - public void printPGFTex( double[][] result ){ + public void printPGFTex( Vector<Vector<Pair>> result ){ int i = 0; String marktable[] = { "+", "*", "x", "-", "|", "o", "asterisk", "star", "oplus", "oplus*", "otimes", "otimes*", "square", "square*", "triangle", "triangle*", "diamond", "diamond*", "pentagon", "pentagon*"}; String colortable[] = { "black", "red", "green", "blue", "cyan", "magenta", "yellow" } ; @@ -311,26 +369,27 @@ public class GenPlot { output.println("% Draw grid"); output.println("\\draw[step="+(STEP/10)+"cm,very thin,color=gray] (-0.2,-0.2) grid ("+STEP+","+STEP+");"); output.println("\\draw[->] (-0.2,0) -- (10.2,0);"); - // JE2010GRAPH - output.println("\\draw (5,-0.3) node {$recall$}; "); + output.println("\\draw (5,-0.3) node {$"+xlabel+"$}; "); output.println("\\draw (0,-0.3) node {0.}; "); output.println("\\draw (10,-0.3) node {1.}; "); output.println("\\draw[->] (0,-0.2) -- (0,10.2);"); output.println("\\draw (-0.3,0) node {0.}; "); - // JE2010GRAPH - output.println("\\draw (-0.3,5) node[rotate=90] {$precision$}; "); + output.println("\\draw (-0.3,5) node[rotate=90] {$"+ylabel+"$}; "); output.println("\\draw (-0.3,10) node {1.}; "); output.println("% Plots"); for ( String m : listAlgo ) { - output.println("\\draw["+colortable[i%7]+"] plot[mark="+marktable[i%19]+",smooth] file {"+m+".table};"); + output.println("\\draw["+colortable[i%7]+"] plot[mark="+marktable[i%19]+"] file {"+m+".table};"); + //,smooth i++; } // And a legend output.println("% Legend"); i = 0; for ( String m : listAlgo ) { - output.println("\\draw["+colortable[i%7]+"] plot[mark="+marktable[i%19]+",smooth] coordinates {("+((i%3)*3+1)+","+(-(i/3)*.8-1)+") ("+((i%3)*3+3)+","+(-(i/3)*.8-1)+")};"); + output.println("\\draw["+colortable[i%7]+"] plot[mark="+marktable[i%19]+"] coordinates {("+((i%3)*3+1)+","+(-(i/3)*.8-1)+") ("+((i%3)*3+3)+","+(-(i/3)*.8-1)+")};"); + //,smooth output.println("\\draw["+colortable[i%7]+"] ("+((i%3)*3+2)+","+(-(i/3)*.8-.8)+") node {"+m+"};"); + output.printf("\\draw["+colortable[i%7]+"] ("+((i%3)*3+2)+","+(-(i/3)*.8-1.2)+") node {%1.2f};\n", listEvaluators.get(i).getGlobalResult() ); i++; } output.println("\\end{tikzpicture}"); @@ -338,7 +397,8 @@ public class GenPlot { output.println("\\end{document}"); i = 0; - for ( String algo : listAlgo ) { + for( Vector<Pair> table : result ) { + String algo = listAlgo.get(i); // Open one file PrintWriter writer; try { @@ -358,10 +418,9 @@ public class GenPlot { writer.println("%% \\draw plot[mark=+,smooth] file {"+algo+".table};"); writer.println("%% \\end{tikzpicture}"); writer.println(); - // JE2010GRAPH: it outputs the plot by itself !!!! - for( int j = 0; j <= STEP; j++ ){ - writer.print((double)j*10/STEP); - writer.println(" "+result[i][j]*10); + for( Pair p : table ) { + if ( debug > 1 ) System.err.println( " >> "+p.getX()+" - "+p.getY() ); + writer.println( p.getX()*10+" "+ p.getY()*10 ); } writer.close(); } catch (Exception ex) { ex.printStackTrace(); } @@ -370,28 +429,31 @@ public class GenPlot { } } - public void printTSV( double[][] result ) { + // 2010: THIS IS ONLY FOR TSV ET CA NE MARCHE PAS A + // IT IS SUPPOSED TO PROVIDE + // List of algo + // List of STEP + points + public void printTSV( Vector<Vector<Pair>> points ) { // Print first line for ( String m : listAlgo ) { output.print("\t"+m ); } - output.println(); - // Print others - for( int j = 0; j <= STEP; j++ ){ - output.print((double)j/STEP); - for( int i = 0; i < listAlgo.size(); i++ ){ - output.print("\t"+result[i][j]); - } - output.println(); + for ( int i= 0; i < 100 ; i += STEP ) { + for( int j = 0; j < size; j++ ){ + Pair precrec = points.get(j).get(i); + output.println( precrec.getX()+" "+precrec.getY() ); } + } + output.println(); } public void usage() { System.out.println("usage: GenPlot [options]"); System.out.println("options are:"); System.out.println("\t--type=tsv|tex|(html|xml) -t tsv|tex|(html|xml)\tSpecifies the output format"); - System.out.println("\t--evaluator=class -e class\tSpecifies the class of GraphEvaluator to be used"); + System.out.println("\t--graph=class -g class\tSpecifies the class of Evaluator to be used"); + System.out.println("\t--evaluator=class -e class\tSpecifies the class of GraphEvaluator (plotter) to be used"); System.out.println("\t--list=algo1,...,algon -l algo1,...,algon\tSequence of the filenames to consider"); System.out.println("\t--debug[=n] -d [n]\t\tReport debug info at level n"); System.out.println("\t--help -h\t\t\tPrint this message");