Skip to content

Commit

Permalink
Merge pull request #6 from phyloref/rewrote_phyloref_resolution_testing
Browse files Browse the repository at this point in the history
The current version of the phyloreference testing code had evolved through several different cycles, was badly written, and appeared to not be correctly testing phyloreferences using the expected_phyloreference_named property. I replaced it with cleaner, easier to read code that appears to be testing phyloreferences correctly, and tested these in PR #9 and its use in phyloref/clade-ontology#29.
  • Loading branch information
gaurav authored Jun 20, 2018
2 parents 2f5ee2e + 97e0676 commit e26ab47
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 118 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Change log for JPhyloref

Based on the suggestion at https://keepachangelog.com/en/1.0.0/.

## [Unreleased]

## 0.1 - 2018-06-20
- Initial release, with support for testing phyloreferences expressed in OWL
and stored in RDF/XML.

[Unreleased]: https://github.com/phyloref/jphyloref/compare/v0.1...HEAD
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>org.phyloref</groupId>
<artifactId>jphyloref</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>jphyloref</name>
Expand Down
76 changes: 52 additions & 24 deletions src/main/java/org/phyloref/jphyloref/JPhyloRef.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,30 @@
import org.phyloref.jphyloref.commands.TestCommand;

/**
* Main class for JPhyloRef. Figures out what the user
* wants us to do.
* Main class for JPhyloRef. Contains a list of Commands,
* as well as the code for determining which Command to
* execute.
*
*/
public class JPhyloRef
{
public static final String VERSION = "0.0.1-SNAPSHOT";
/** Version of JPhyloRef */
public static final String VERSION = "0.1-SNAPSHOT";

/** List of all commands included in JPhyloRef */
private List<Command> commands = Arrays.asList(
new HelpCommand(),
new TestCommand(),
new ReasonCommand()
);

public static void main( String[] args )
{
JPhyloRef jphyloref = new JPhyloRef();
jphyloref.execute(args);
}
/**
* Interpret the command line arguments to determine which command
* to execute.
*
* @param args Command line arguments
*/
public void execute(String[] args) {
System.err.println("jphyloref/" + VERSION + "\n");

// Prepare to parse command line arguments.
Options opts = new Options();
for(Command cmd: commands) {
Expand All @@ -52,42 +53,69 @@ public void execute(String[] args) {
return;
}

// The first unprocessed argument should be the command.
// Are there any command line arguments?
if(cmdLine.getArgList().isEmpty()) {
// No command provided! Activate help.
// No command line arguments -- display help!
HelpCommand help = new HelpCommand();
help.execute(cmdLine);
} else {
// The first unprocessed argument should be the command.
String command = cmdLine.getArgList().get(0);


// Look for a Command with the name specified.
for(Command cmd: commands) {
if(cmd.getName().equalsIgnoreCase(command)) {
// match!
// Found a match!
cmd.execute(cmdLine);
System.exit(0);
return;
}
}

// Could not find any command.
System.err.println("Error: command '" + command + "' has not been implemented.");
System.exit(1);
return;
}
}

/**
* Main method for JPhyloRef. Creates the JPhyloRef instance
* and tells it to start processing the command line arguments.
*
* @param args Command line arguments
*/
public static void main( String[] args )
{
JPhyloRef jphyloref = new JPhyloRef();
jphyloref.execute(args);
}

/**
* HelpCommand is a special command that lists help information on all currently
* implemented Commands. You can activate it by running "jphyloref help" or by
* just entering "jphyloref" without any command.
*/
private class HelpCommand implements Command {
/** This command is named "help", and so can be executed as "jphyloref help" */
public String getName() { return "help"; }

/** Returns a description of the Help command */
public String getDescription() { return "Provides help on all jphyloref commands"; }

/** There are no command line options for the Help command. */
public void addCommandLineOptions(Options opts) { }

/** Display a list of Commands that can be executed on the command line. */
public void execute(CommandLine cmdLine) {
// No arguments are provided.

System.err.println("Synopsis: jphyloref <command> <options>\n");
// Display a synopsis.
System.out.println("Synopsis: jphyloref <command> <options>\n");

System.err.println("Where command is one of:");
// Display a list of currently included Commands.
System.out.println("Where command is one of:");
for(Command cmd: commands) {
System.err.println(" - " + cmd.getName() + ": " + cmd.getDescription());
// Display the description of the command.
System.out.println(" - " + cmd.getName() + ": " + cmd.getDescription());

// Display all the command line options supported by that command.
Options opts = new Options();
cmd.addCommandLineOptions(opts);

Expand All @@ -96,15 +124,15 @@ public void execute(CommandLine cmdLine) {
if(opt.getLongOpt() != null)
longOpt = ", " + opt.getLongOpt();

System.err.println(" - "
System.out.println(" - "
+ opt.getOpt() + longOpt + ": "
+ opt.getDescription()
);
}
}

// One final blank line, please.
System.err.println("");
System.out.println("");
}
}
}
125 changes: 58 additions & 67 deletions src/main/java/org/phyloref/jphyloref/commands/TestCommand.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
package org.phyloref.jphyloref.commands;

import org.phyloref.jphyloref.helpers.PhylorefHelper;
import org.phyloref.jphyloref.helpers.OWLHelper;

import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.phyloref.jphyloref.helpers.OWLHelper;
import org.phyloref.jphyloref.helpers.PhylorefHelper;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.OWLAnnotationProperty;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
Expand All @@ -39,25 +33,25 @@
import org.tap4j.producer.TapProducerFactory;
import org.tap4j.util.DirectiveValues;
import org.tap4j.util.StatusValues;

import uk.ac.manchester.cs.jfact.JFactReasoner;
import uk.ac.manchester.cs.jfact.kernel.options.JFactReasonerConfiguration;

/**
* Test whether the phyloreferences in the provided ontology resolve correctly.
* This currently supports RDF/XML input only, but we will eventually modify
* this to support PHYX files directly.
*
* At the moment, this works on OWL ontologies, but there's really no reason we
* couldn't test the labeled.json file directly! Maybe at a later date?
*
* I can't resist using the Test Anything Protocol here, which has nice
* libraries in both Python and Java.
* I use the Test Anything Protocol here, which has nice libraries in both
* Python and Java.
*
* @author Gaurav Vaidya <[email protected]>
*
*/
public class TestCommand implements Command {
/**
* This command is named "test". It should be
* involved "java -jar jphyloref.jar test ..."
* This command is named Test. It should be
* invoked as "java -jar jphyloref.jar test ..."
*/

@Override
Expand All @@ -66,6 +60,8 @@ public String getName() {
}

/**
* A description of the Test command.
*
* @return A description of this command.
*/
@Override
Expand All @@ -74,7 +70,8 @@ public String getDescription() {
}

/**
* Add command-line options specific to this command.
* Add command-line options specific to this command. There is only one:
* -i or --input can be used to set the RDF/XML file to read.
*
* @param opts The command-line options to modify for this command.
*/
Expand Down Expand Up @@ -146,7 +143,7 @@ public void execute(CommandLine cmdLine) throws RuntimeException {
// Get some properties ready before-hand so we don't have to reload
// them on every loop.
OWLAnnotationProperty labelAnnotationProperty = dataFactory.getOWLAnnotationProperty(OWLRDFVocabulary.RDFS_LABEL.getIRI());
OWLDataProperty expectedPhyloreferenceNameProperty = dataFactory.getOWLDataProperty(PhylorefHelper.IRI_NAME_OF_EXPECTED_PHYLOREF);
OWLDataProperty expectedPhyloreferenceNamedProperty = dataFactory.getOWLDataProperty(PhylorefHelper.IRI_NAME_OF_EXPECTED_PHYLOREF);
OWLObjectProperty unmatchedSpecifierProperty = dataFactory.getOWLObjectProperty(PhylorefHelper.IRI_PHYLOREF_UNMATCHED_SPECIFIER);
// OWLDataProperty specifierDefinitionProperty = dataFactory.getOWLDataProperty(PhylorefHelper.IRI_CLADE_DEFINITION);

Expand Down Expand Up @@ -178,7 +175,7 @@ public void execute(CommandLine cmdLine) throws RuntimeException {
phylorefLabel = phyloref.getIRI().toString();
result.setDescription("Phyloreference '" + phylorefLabel + "'");

// Which nodes did this phyloreference resolved to?
// Which nodes did this phyloreference resolve to?
OWLClass phylorefAsClass = manager.getOWLDataFactory().getOWLClass(phyloref.getIRI());
Set<OWLNamedIndividual> nodes = reasoner.getInstances(phylorefAsClass, false).getFlattened();

Expand All @@ -188,59 +185,53 @@ public void execute(CommandLine cmdLine) throws RuntimeException {
result.addComment(new Comment("No nodes matched."));
testFailed = true;
} else {
// Make a list of every expected phyloreference for input node.
Map<OWLNamedIndividual, Set<OWLLiteral>> expectedPhyloreferencesByNode = new HashMap<>();

// Look for nodes where either its label or its expected phyloreference label
// is equal to the phyloreference we are currently processing.
Set<String> nodeLabelsWithExpectedPhylorefs = new HashSet<>();
Set<String> nodeLabelsWithoutExpectedPhylorefs = new HashSet<>();

for(OWLNamedIndividual node: nodes) {
// What are the expected phyloreferences associated with these nodes?
expectedPhyloreferencesByNode.put(
node,
node.getDataPropertyValues(expectedPhyloreferenceNameProperty, ontology)
);
// Get a list of all expected phyloreference labels from the OWL file.
Set<String> expectedPhylorefsNamed = node.getDataPropertyValues(expectedPhyloreferenceNamedProperty, ontology)
.stream()
.map(literal -> literal.getLiteral()) // We ignore languages for now.
.collect(Collectors.toSet());

// Add the label of the node as well.
Set<String> nodeLabels = OWLHelper.getLabelsInEnglish(node, ontology);
expectedPhylorefsNamed.addAll(nodeLabels);

// Build a new node label that describes this node.
String nodeLabel = new StringBuilder()
.append("[")
.append(String.join(", ", expectedPhylorefsNamed))
.append("]")
.toString();

// Does this node have an expected phyloreference identical to the phyloref being tested?
if(expectedPhylorefsNamed.contains(phylorefLabel)) {
nodeLabelsWithExpectedPhylorefs.add(nodeLabel);
} else {
nodeLabelsWithoutExpectedPhylorefs.add(nodeLabel);
}
}

// Flatten expected phyloreference names from each Node into a
// single set of unique expected phyloreference names.
Set<OWLLiteral> distinctExpectedPhylorefNames = expectedPhyloreferencesByNode.values()
.stream().flatMap(n -> n.stream())
.collect(Collectors.toSet());

// How many distinct expected phyloref names do we have?
if(distinctExpectedPhylorefNames.isEmpty()) {
result.addComment(new Comment("None of the " + nodes.size() + " resolved nodes are expected to resolve to phyloreferences."));
// What happened?
if(!nodeLabelsWithExpectedPhylorefs.isEmpty() && !nodeLabelsWithoutExpectedPhylorefs.isEmpty()) {
// We found nodes that expected this phyloref, as well as nodes that did not -- success!
result.addComment(new Comment("The following nodes were matched and expected this phyloreference: " + String.join("; ", nodeLabelsWithExpectedPhylorefs)));
result.addComment(new Comment("Also, the following nodes were matched but did not expect this phyloreference: " + String.join("; ", nodeLabelsWithoutExpectedPhylorefs)));
} else if(!nodeLabelsWithExpectedPhylorefs.isEmpty()) {
// We only found nodes that expected this phyloref -- success!
result.addComment(new Comment("The following nodes were matched and expected this phyloreference: " + String.join("; ", nodeLabelsWithExpectedPhylorefs)));
} else if(!nodeLabelsWithoutExpectedPhylorefs.isEmpty()) {
// We only have nodes that did not expect this phyloref -- failure!
result.addComment(new Comment("The following nodes were matched but did not expect this phyloreference: " + String.join("; ", nodeLabelsWithoutExpectedPhylorefs)));
testFailed = true;

} else if(distinctExpectedPhylorefNames.size() > 1) {
// This is okay IF at least one of the nodes is expected to resolve to this phyloreference.
List<String> otherLabels = new LinkedList<>();

int matchCount = 0;
for(OWLLiteral label: distinctExpectedPhylorefNames) {
if(label.getLiteral().equals(phylorefLabel)) matchCount++;
else otherLabels.add(label.getLiteral());
}

String otherLabelsStr = otherLabels.stream().collect(Collectors.joining("; "));

if(matchCount > 0) {
result.addComment(new Comment("Phyloreference resolved to " + matchCount + " nodes with the expected phyloreference label '" + phylorefLabel + "'; other nodes expected phyloreferences: " + otherLabelsStr));
} else {
result.addComment(new Comment("Phyloreference did not resolve to the expected phyloreference label '" + phylorefLabel + "', but did resolve to multiple expected phyloreferences: " + otherLabelsStr));
testFailed = true;
}

} else {
// We have exactly one expected phyloref name -- but is it the right one?
OWLLiteral onlyOne = distinctExpectedPhylorefNames.iterator().next();
String label = onlyOne.getLiteral();

if(label.equals(phylorefLabel)) {
result.addComment(new Comment("Resolved node label '" + label + "' identical to expected phyloreference label '" + phylorefLabel + "'"));
} else {
result.addComment(new Comment("Resolved node label '" + label + "' differs from expected phyloreference label '" + phylorefLabel + "'"));
testFailed = true;
}
}
// No nodes matched. This should have been caught earlier, but just in case.
throw new RuntimeException("No nodes were matched, which should have been caught earlier. Programmer error!");
}
}

// Look for all unmatched specifiers reported for this phyloreference
Expand Down
Loading

0 comments on commit e26ab47

Please sign in to comment.