This tutorial has been initially designed for the Alignment API 4.0 using Jena, Pellet and IDDL.
It has been reengineered in the Alignment API 4.5 to use Jena, HermiT
1.3.8, IDDL (now DRAon) 1.4 and LogMap 2.
Preparation
Just:
$ cd tutorial4
Data
We have two ontologies, ontology1.owl
and ontology2.owl, under which two sets of
students are described. Unfortunately, the
administration started to record participants with their own ontology
before finding that using FOAF
would be a better idea. We now end up with two incomplete lists of
participants.
The goal is to have a unified view of these participants. For that
purpose, we will match the two ontologies and reason with the result
in order to view participants from boths ontologies under the other
ontology. This is not a difficult task, the goal here is only to show
how this could be achieved.
For that purpose, you have to develop a program in Java.
We first define the CLASSPATH because a lot of external software is required
String RESTServ = "http://aserv.inrialpes.fr/rest/";
String myId = "Test";
Alignment al = null;
URI uri1 = null;
URI uri2 = null;
String u1 = "file:ontology1.owl";
String u2 = "file:ontology2.owl";
String method = "fr.inrialpes.exmo.align.impl.method.StringDistAlignment";
Properties params = new BasicParameters();
try {
uri1 = new URI( u1 );
uri2 = new URI( u2 );
} catch (URISyntaxException use) { use.printStackTrace(); }
The programme will invoke the alignment server:
// (Sol1) Try to find an alignment between two ontologies from the server
// ask for it
String found = getFromURLString( RESTServ+"find?onto1="+u1+"&onto2="+u2, false );
Retrieve the alignment itself:
// retrieve it
// If there exists alignments, ask for the first one
NodeList alset = extractFromResult( found, "//findResponse/alignmentList/alid[1]/text()", false );
And parse it:
// parse it as an alignment
AlignmentParser aparser = new AlignmentParser(0);
Alignment alu = aparser.parseString( xmlString );
al = ObjectAlignment.toObjectAlignment((URIAlignment)alu);
After introducing the main variables:
using a matcher on the alignment server
Write a program that does try to find an alignment on the alignment
server https://aserv.inrialpes.fr
and, if none is found, computes one.
// (Sol3) Match the ontologies on the server
if ( alset.getLength() == 0 ) {
// call for matching
String match = getFromURLString( RESTServ+"match?onto1="+u1+"&onto2="+u2+"&method="+method+"&pretty="+myId+"&action=Match", true );
}
Just create an instance of AlignmentProcess and call it:
// (Sol2) Match the ontologies with a local algorithm
if ( al == null ){ // Unfortunatelly no alignment was available
AlignmentProcess ap = new StringDistAlignment();
ap.init( uri1, uri2 );
params.setProperty("stringFunction","smoaDistance");
params.setProperty("noinst","1");
ap.align( (Alignment)null, params );
al = ap;
}
The remainder is the same as in the first solution.
More work: You can also store localy computed alignments on
the alignment server.
Not ready yet (but not difficult)
Merge ontologies and test consistency
Generating an OWL ontology merging the two ontologies
Generating OWL axioms from the alignments and merge the ontologies can be done with the alignment API support.
Considering that the alignment has been stored in a file:
alignment.rdf, this can be obtained locally:
Considering that the merged ontologies in a file: results/alignment.owl,
and that you have downloaded HermiT (the Command line version of
hermit requires gnu getopt which is not shipped anymore with the
Alignment API):
$ java -jar HermiT.jar results/alignment.owl -U
which displays
Exception in thread "main" org.semanticweb.owlapi.reasoner.InconsistentOntologyException: Inconsistent ontology
at org.semanticweb.HermiT.Reasoner.throwInconsistentOntologyExceptionIfNecessary(Unknown Source)
at org.semanticweb.HermiT.Reasoner.checkPreConditions(Unknown Source)
at org.semanticweb.HermiT.Reasoner.isSatisfiable(Unknown Source)
at org.semanticweb.HermiT.cli.CommandLine$SatisfiabilityAction.run(Unknown Source)
at org.semanticweb.HermiT.cli.CommandLine.main(Unknown Source)
Create Reasoner instance and load the merged ontologies:
// Test consistency
if ( reasoner.isConsistent() ) {
System.out.println( " *** The aligned ontologies are consistent" );
} else {
System.out.println( " *** The aligned ontologies are inconsistent" );
return;
}
It is even possible to test the satisfiability of classes:
// Test coherence
for ( OWLClass cl : ontology.getClassesInSignature( true ) ) {
if ( !reasoner.isSatisfiable( cl ) ) {
System.out.println( cl+" is incoherent" );
}
}
Load the two ontologies and the alignment in the IDDL reasoner:
ArrayList allist = new ArrayList();
allist.add( al );
IDDLReasoner dreasoner = new IDDLReasoner( allist, Semantics.DL );
Test consistency and check if a particular correspondence is a consequence:
if ( dreasoner.isConsistent() ) {
System.out.println( "IDDL: the alignment network is consistent");
} else {
System.out.println( "IDDL: the alignment network is inconsistent");
}
Repairing an alignment with Alcomo or LogMap repair
Loading ontologies...
IRI: https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology1.owl
IRI: https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology2.owl
...Done
Read RDF Align mapping objects: 11
Num original mappings: 11
Error keeping mappings...
Num repaired mappings: 10
TOTAL REPAIR TIME (s): 0.505
The result can be found in results/mappings_repaired_with_LogMap.rdf.
It contains only 10 of the initial correspondences.
Unfortunately, LogMap outputs wrong alignments (two occurence of
ontology1 in the header must be replaced by ontology2). Once this
fixed, it is possible to perform again:
OWLOntology onto1 = manager.loadOntology(IRI.create("https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology1.owl"));
OWLOntology onto2 = manager.loadOntology(IRI.create("https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology2.owl"));
MappingsReaderManager readermanager = new MappingsReaderManager( "alignment.rdf", "RDF");
Set&ly;MappingObjectStr> input_mappings = readermanager.getMappingObjects();
LogMap2_RepairFacility logmap2_repair = new LogMap2_RepairFacility( onto1, onto2, input_mappings, false, false);
//Set of mappings repaired by LogMap
Set<MappingObjectStr> repaired_mappings = logmap2_repair.getCleanMappings();
Should return similar output as the command line.
Testing subsumption
It is now possible to check that the newly generated alignment is
consistent. Hence we can check some entailment results.
Considering that the merged ontologies in a file: results/alignment.owl,
and that you have uploaded HermiT (the Command line version of
hermit requires gnu getopt which is not shipped anymore with the
Alignment API):
All sub-classes of 'https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology1.owl#Estudiante':
owl:Nothing
<https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology1.owl#TutorEstudiante>
Classes equivalent to '<https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology1.owl#Estudiante>':
<https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology1.owl#Estudiante>
<https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology2.owl#Student>
All sub-classes of 'https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology2.owl#Student':
owl:Nothing
<https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology1.owl#TutorEstudiante>
Classes equivalent to '<https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology2.owl#Student>':
<https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology1.owl#Estudiante>
<https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology2.owl#Student>
Create Reasoner instance and load the merged ontologies as before
and perform some subsumption tests:
Starting from where we were with the HermiT reasonner, it is
possible to retrive the instances of "Estudiantes" and to
manipulate them:
// get the instances of a class
OWLClass estud = manager.getOWLDataFactory().getOWLClass( IRI.create( "https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology1.owl#Estudiante" ) );
OWLClass person = manager.getOWLDataFactory().getOWLClass( IRI.create( "https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology2.owl#Person" ) );
OWLClass student = manager.getOWLDataFactory().getOWLClass( IRI.create( "https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology2.owl#Student" ) );
Set<OWLNamedIndividual> instances = reasoner.getInstances( estud, false ).getFlattened();
System.err.println("OWLReasoner(Merged): There are "+instances.size()+" students ("+clname(estud)+")");
This should output:
OWLReasoner(Merged): There are 47 students (Estudiante)
Transforming queries
The following solutions use the query in the
file query.sparql which is expressed with respect to ontology1.owl.
The query can be transformed on command line through:
$ java -cp $CLASSPATH fr.inrialpes.exmo.align.cli.TransformQuery -a file:alignment.rdf -q query.sparql -Donto2=https://moex.gitlabpages.inria.fr/alignapi/tutorial/tutorial4/ontology2.owl# -o results/query2.sparql
$ cp ontology2.owl results/ontology2.rdf # because arq does not owl as input
$ java -cp $CLASSPATH arq.query --query results/query2.sparql --data results/ontology2.rdf
The result will be:
--------------------------------------------------------------------------------------------------------------
| fn | ln | t | s |
==============================================================================================================
| "Archie" | "Shepp" | "Occarina University" | |
| "Sonny" | "Rollins" | "Universita di Miggliore" | "M. Pecorino" |
| "Astrud" | "Gilberto" | "Universität der Gemütlichkeit" | "Pr. Dr. M. Coppa" |
| "Peggy" | "Lee" | "University des Mitteleuropa" | "a.Univ.Prof.DI.Dr. Z. Melone" |
| "Glenn" | "Miller" | "University of Shepperington" | "Professor G. Limone" |
| "Rickie Lee" | "Jones" | "Philip Vanderbilt Universiteit" | "A. Lambretta" |
| "Rickie Lee" | "Jones" | "Velo Research" | "A. Lambretta" |
| "Lionel" | "Hampton" | "Politecnico di Madalena" | "O. Mascarponne" |
| "Lionel" | "Hampton" | "Politecnico di Madalena" | "A. Gorgonzola" |
| "Eleonore" | "Rigby" | "Politecnico di Bermudas" | |
| "Diana" | "Krall" | "Royal University of Worchester sauce" | "Dr. A. Verdura" |
| "Diana" | "Krall" | "Royal University of Worchester sauce" | "C. Fragola" |
| "Dave" | "Brubeck" | "University of Namibia" | "P. Tiramissu" |
| "Dave" | "Brubeck" | "University of Namibia" | "G. Belladonna" |
| "Milt" | "Jackson" | "Escena" | |
| "Julian" | "Aldderley" | "Politecnico di Madalena" | |
| "Betty" | "Sinclair" | "Institute for Social Coworkers" | |
| "Cilla" | "Black" | "University of Shepperington" | "Prof. G. Limone" |
| "Natalie" | "Merchant" | "JOHANNISBEER RESEARCH" | |
| "Django" | "Reinhardt" | "IRIA Saskatchevan" | "Prof. Chr. Melocoton" |
| "Django" | "Reinhardt" | "University Pie XXIII" | "Prof. Chr. Melocoton" |
| "Ray" | "Bryant" | "Handshuh University" | |
| "Jackie" | "McLean" | "Polytechnic of Madeira" | "S. Zucchini" |
| "Art" | "Tatum" | "National Kapodistrian University of Athens" | "B. Tiramisu" |
| "Duke" | "Ellington" | "Vanilla University of Technology" | "Prof. G. Cetriolo" |
| "Chick" | "Corea" | "University of Ausblick" | "Prof. Dr. A. Belladonna" |
| "Kenny" | "Burrell" | "Politecnico di Belladona" | "S. Proscuitto" |
| "Joe" | "Zawinul" | "Institute for Social Coworkers" | |
| "Dexter" | "Gordon" | "Eelberg University" | "C. Pannacotta" |
--------------------------------------------------------------------------------------------------------------
This can be achieved by using Jena ARQ query evaluator:
Model model = ModelFactory.createOntologyModel( OntModelSpec.OWL_DL_MEM_RULE_INF, null );
// Load ontology 1
model.read( "file:ontology1.owl" );
// Query in ontology 1
QueryExecution qe = QueryExecutionFactory.create( QueryFactory.read( "file:query.sparql" ), model );
ResultSet results = qe.execSelect();
// Output query results
ResultSetFormatter.out(System.out, results, query);
// Load ontology 2
model = ModelFactory.createOntologyModel( OntModelSpec.OWL_DL_MEM_RULE_INF, null );
model.read( "file:ontology2.owl" );
// Transform query
String transformedQuery = null;
try {
InputStream in = new FileInputStream( "query.sparql" );
BufferedReader reader = new BufferedReader( new InputStreamReader(in) );
String line = null;
String queryString = "";
while ((line = reader.readLine()) != null) {
queryString += line + "\n";
}
Properties parameters = new Properties();
transformedQuery = ((BasicAlignment)al).rewriteQuery( queryString, parameters );
} catch ( Exception ex ) { ex.printStackTrace(); }
// Query ontology 2
displayQueryAnswer( model, QueryFactory.create( transformedQuery ) );
qe = QueryExecutionFactory.create( QueryFactory.create( transformedQuery ), model );
results = qe.execSelect();
// Output query results
ResultSetFormatter.out(System.out, results, query);
This gives the result:
-------------------------------------------------------------------------------------------------------
| fn | ln | t | s |
=======================================================================================================
| "Miles" | "Davis" | "JOHANNISBEER RESEARCH" | "Univ.-Prof. Dr. Paolo Carciofo" |
| "Chet" | "Baker" | "University Pie XXIII" | |
| "McCoy" | "Tyner" | "University of Soupaloignon" | "Dr Nanni Girasole" |
| "Laurent" | "De Wilde" | "University of Sussex" | "Ugo Calamari" |
| "Laurent" | "De Wilde" | "University of Sussex" | "Simona Riccota" |
| "Charlie" | "Parker" | "Institut Von Humboldt" | "Cecilia Parmiggiano" |
| "Carmen" | "McRae" | "University of the Rainforest" | "Mario Staggioni" |
| "Benny" | "Goodman" | "The Univesity of the true way" | "Prof. Dr. Riccardo Peperoni" |
| "Thelonious" | "Monk" | "Madeira Instutute of Technology" | "Stefano Zucchini" |
| "Ella" | "Fitzgerald" | "Vanilla University of Technology" | "Prof. Giancarlo Cetriolo" |
| "Janis" | "Joplin" | "University of Zoulouland" | "Fiorenta Pescadore" |
| "Janis" | "Joplin" | "University of Zoulouland" | "Medusa Pesto" |
| "Bud" | "Powell" | "Cogebom" | "Giovanni Dolomitti" |
| "Bud" | "Powell" | "SPAM" | "Giovanni Dolomitti" |
| "John" | "Coltrane" | "Estrellas" | |
| "Aretha" | "Franklin" | "University of Huelva" | |
| "Carla" | "Bley" | "none" | "Christina Melocoton" |
| "Cab" | "Calloway" | "University of Shepperington" | |
| "Stan" | "Getz" | "Altacola research centre" | "Prof. Pierpaolo Polenta" |
| "Art" | "Blackey" | "University of Albatra" | "Pierluiggi Pomodoro" |
| "Art" | "Blackey" | "University of Albatra" | "Paola Pomodoro" |
| "Nina" | "Simone" | "Research Center of the Americas" | "Domenica Melanzana" |
| "Ornette" | "Coleman" | "Trinidad College Dubrovnik" | "Carla Cipolla" |
| "Billie" | "Holliday" | "University of Soupaloignon" | "Mr. Laurenzo Girafiore" |
| "Billie" | "Holliday" | "University of Soupaloignon" | "Dr.Federico diGuava" |
| "Oscar" | "Peterson" | "Institute for Applied Boxology" | "Dott. Antolio Bresaola" |
| "Coleman" | "Hawkins" | "University of South Poland" | "Pepe Frutti di Mare" |
| "John" | "Zorn" | "Altacola research centre" | "Prof. Pierpaolo Polenta" |
-------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------
| fn | ln | t | s |
==============================================================================================================
| "Archie" | "Shepp" | "Occarina University" | |
| "Sonny" | "Rollins" | "Universita di Miggliore" | "M. Pecorino" |
| "Astrud" | "Gilberto" | "Universität der Gemütlichkeit" | "Pr. Dr. M. Coppa" |
| "Peggy" | "Lee" | "University des Mitteleuropa" | "a.Univ.Prof.DI.Dr. Z. Melone" |
| "Glenn" | "Miller" | "University of Shepperington" | "Professor G. Limone" |
| "Rickie Lee" | "Jones" | "Philip Vanderbilt Universiteit" | "A. Lambretta" |
| "Rickie Lee" | "Jones" | "Velo Research" | "A. Lambretta" |
| "Lionel" | "Hampton" | "Politecnico di Madalena" | "O. Mascarponne" |
| "Lionel" | "Hampton" | "Politecnico di Madalena" | "A. Gorgonzola" |
| "Eleonore" | "Rigby" | "Politecnico di Bermudas" | |
| "Diana" | "Krall" | "Royal University of Worchester sauce" | "Dr. A. Verdura" |
| "Diana" | "Krall" | "Royal University of Worchester sauce" | "C. Fragola" |
| "Dave" | "Brubeck" | "University of Namibia" | "P. Tiramissu" |
| "Dave" | "Brubeck" | "University of Namibia" | "G. Belladonna" |
| "Milt" | "Jackson" | "Escena" | |
| "Julian" | "Aldderley" | "Politecnico di Madalena" | |
| "Betty" | "Sinclair" | "Institute for Social Coworkers" | |
| "Cilla" | "Black" | "University of Shepperington" | "Prof. G. Limone" |
| "Natalie" | "Merchant" | "JOHANNISBEER RESEARCH" | |
| "Django" | "Reinhardt" | "IRIA Saskatchevan" | "Prof. Chr. Melocoton" |
| "Django" | "Reinhardt" | "University Pie XXIII" | "Prof. Chr. Melocoton" |
| "Ray" | "Bryant" | "Handshuh University" | |
| "Jackie" | "McLean" | "Polytechnic of Madeira" | "S. Zucchini" |
| "Art" | "Tatum" | "National Kapodistrian University of Athens" | "B. Tiramisu" |
| "Duke" | "Ellington" | "Vanilla University of Technology" | "Prof. G. Cetriolo" |
| "Chick" | "Corea" | "University of Ausblick" | "Prof. Dr. A. Belladonna" |
| "Kenny" | "Burrell" | "Politecnico di Belladona" | "S. Proscuitto" |
| "Joe" | "Zawinul" | "Institute for Social Coworkers" | |
| "Dexter" | "Gordon" | "Eelberg University" | "C. Pannacotta" |
--------------------------------------------------------------------------------------------------------------
There are indeed 47 individuals (but more occurences in the table due
to multiple supervisors).
Querying with the merged ontology
In case you go for SPARQL please, take care of the inference regime
and observe what are the differences in this case. You can of course
run various queries and start by running them in one of the initial
ontologies instead of the merged one.
This does not work, either in command line or in Java, though it has
worked in the past. Apparently, Jena does not properly load the
imported ontologies. Anyone knowing the solution will be
-------------------
| fn | ln | t | s |
===================
-------------------
Load the merged ontology under Jena:
// (Sol1) Use SPARQL to answer queries (at the data level)
InputStream in = new FileInputStream( merged );
Model model = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM_RULE_INF,null);
model.read( in, "file:"+merged.getPath() );
in.close();
Read the query (please play by changing the query)
// Create a new query
Query query = QueryFactory.read( "file:query.sparql" );
Evaluation:
// Execute the query and obtain results
QueryExecution qe = QueryExecutionFactory.create(query, model);
ResultSet results = qe.execSelect();
// Output query results
ResultSetFormatter.out(System.out, results, query);
The results for this evaluation is: (SHOULD BE)
---------------------------------------------------------------------------------------
| fn | ln | t | s |
=======================================================================================
| "Miles" | "Davis" | "JOHANNISBEER RESEARCH" | "Univ.-Prof. Dr. Paolo Carciofo" |
| "Chet" | "Baker" | "University Pie XXIII" | |
| "McCoy" | "Tyner" | "University of Soupaloignon" | "Dr Nanni Girasole" |
...
Generating SPARQL constructs
A SPARQL construct query may be use for extracting data expressed
in one ontology and generate this data with respect to the other
ontology.
It is possible to generate such constructs from
This is without the recourse to LogMap. The (two) LogMap parts may be
uncommented from the code and the instructions above run again with
logmap2_standalone.jar in the classpath.