I'm new to this ontology. I m using protege now. I have 2 classes BT and Document. I have created 2 Object Property 1. topic (Domain:Document , Range:BT) 2. hasDocument (Inverse property of topic).
I have created 1 DataType Property called title (Domain:Document Range:Literal).
Following are the Samples for the properties which I have created
BT hasDocument Document
Document topic BT
Document title "TestingName"
I dont know how to create a property which infers the following result
BT newProperty "TestingName"
If I understand your question, you're looking to be able to infer from
docX topic someTopic
docX title "SampleTitle"
someTopic hasDocumentWithTitle "SampleTitle"
You could almost do this with an property chain, by asserting that hasDocumentWithTitle has as a subproperty the chain (inverse topic) o title. Unfortunately, in OWL property chains can't end with datatype properties, so you can't do this. However, you can use SWRL rules, and many OWL2 reasoners process SWRL rules. You'd use a rule of the form:
topic(?doc,?topic) ∧ title(?doc,?title) → hasDocumentWithTitle(?topic,?title)
For instance we can get the following result in Protege with the following ontology:
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rule-example="http://www.example.org/rule-example#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:swrl="http://www.w3.org/2003/11/swrl#"
xmlns:swrlb="http://www.w3.org/2003/11/swrlb#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<owl:Ontology rdf:about="http://www.example.org/rule-example"/>
<owl:Class rdf:about="http://www.example.org/rule-example#Document"/>
<owl:Class rdf:about="http://www.example.org/rule-example#Topic"/>
<owl:ObjectProperty rdf:about="http://www.example.org/rule-example#hasTopic"/>
<owl:DatatypeProperty rdf:about="http://www.example.org/rule-example#hasTitle"/>
<owl:DatatypeProperty rdf:about="http://www.example.org/rule-example#hasDocumentWithTitle"/>
<owl:NamedIndividual rdf:about="http://www.example.org/rule-example#doc42">
<rdf:type rdf:resource="http://www.example.org/rule-example#Document"/>
<rule-example:hasTitle>Document Number Forty-Two</rule-example:hasTitle>
<rule-example:hasTopic>
<owl:NamedIndividual rdf:about="http://www.example.org/rule-example#topic101">
<rdf:type rdf:resource="http://www.example.org/rule-example#Topic"/>
<rule-example:hasTopic rdf:resource="http://www.example.org/rule-example#topic101"/>
</owl:NamedIndividual>
</rule-example:hasTopic>
</owl:NamedIndividual>
<swrl:Imp>
<swrl:head>
<swrl:AtomList>
<rdf:rest rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"/>
<rdf:first>
<swrl:DatavaluedPropertyAtom>
<swrl:propertyPredicate rdf:resource="http://www.example.org/rule-example#hasDocumentWithTitle"/>
<swrl:argument2>
<swrl:Variable rdf:about="urn:swrl#title"/>
</swrl:argument2>
<swrl:argument1>
<swrl:Variable rdf:about="urn:swrl#topic"/>
</swrl:argument1>
</swrl:DatavaluedPropertyAtom>
</rdf:first>
</swrl:AtomList>
</swrl:head>
<swrl:body>
<swrl:AtomList>
<rdf:rest>
<swrl:AtomList>
<rdf:rest rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"/>
<rdf:first>
<swrl:DatavaluedPropertyAtom>
<swrl:propertyPredicate rdf:resource="http://www.example.org/rule-example#hasTitle"/>
<swrl:argument1>
<swrl:Variable rdf:about="urn:swrl#doc"/>
</swrl:argument1>
<swrl:argument2 rdf:resource="urn:swrl#title"/>
</swrl:DatavaluedPropertyAtom>
</rdf:first>
</swrl:AtomList>
</rdf:rest>
<rdf:first>
<swrl:IndividualPropertyAtom>
<swrl:propertyPredicate rdf:resource="http://www.example.org/rule-example#hasTopic"/>
<swrl:argument1 rdf:resource="urn:swrl#doc"/>
<swrl:argument2 rdf:resource="urn:swrl#topic"/>
</swrl:IndividualPropertyAtom>
</rdf:first>
</swrl:AtomList>
</swrl:body>
</swrl:Imp>
</rdf:RDF>
Related
This is a simplified extract from my ontology:
<owl:Class rdf:ID="Role" />
<owl:Class rdf:ID="Location" />
<owl:Class rdf:ID="SalaryRange">
<rdfs:subClassOf rdf:resource="#HasLocation" />
</owl:Class>
<owl:Class rdf:ID="HasLocation" />
<owl:ObjectProperty rdf:ID="location">
<rdfs:domain rdf:resource="#HasLocation" />
<rdfs:range rdf:resource="#Location" />
</owl:ObjectProperty>
<owl:DataProperty rdf:ID="salaryRangeOfRole">
<rdfs:domain rdf:resource="#Role" />
<rdfs:range rdf:resource="#SalaryRange" />
</owl:DataProperty>
How can I ensure now that the Locations in the SalaryRanges are unique per Role?
I have read about FunctionalPropertys, but do not see how to use this in this case.
The challenge you have here is that you basically want a unique constraint on Role and Location for SalaryRange. Funtional properties really is defined for a single property - except if you do model some functional property through class expressions.
A simple way to achieve this (if you are using OWL) is through key constraints. I battle a bit to understand your example. I therefore simplified your example further to assuming you have a SalaryRange for which the Role and Location need to be unique. I think if you understand the general idea you will be able to modified it to fit your example exactly.
We have the classes Location and Role as you defined it.
<owl:Class rdf:about="Location"/>
<owl:Class rdf:about="Role"/>
Then I defined object properties role and location follows:
<owl:ObjectProperty rdf:about="location">
<rdfs:domain rdf:resource="SalaryRange"/>
<rdfs:range rdf:resource="Location"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="role">
<rdfs:domain rdf:resource="SalaryRange"/>
<rdfs:range rdf:resource="Role"/>
</owl:ObjectProperty>
Then to ensure that SalaryRange will have unique roles and locations, you can enforce it as follows:
<owl:Class rdf:about="SalaryRange">
<owl:hasKey rdf:parseType="Collection">
<rdf:Description rdf:about="location"/>
<rdf:Description rdf:about="role"/>
</owl:hasKey>
</owl:Class>
You can test this with the following individuals:
<owl:NamedIndividual rdf:about="location1">
<owl:sameAs rdf:resource="location2"/>
</owl:NamedIndividual>
<owl:NamedIndividual rdf:about="location2"/>
<owl:NamedIndividual rdf:about="role1">
<owl:sameAs rdf:resource="role2"/>
</owl:NamedIndividual>
<owl:NamedIndividual rdf:about="role2"/>
<owl:NamedIndividual rdf:about="salaryRange1">
<location rdf:resource="location1"/>
<role rdf:resource="role1"/>
</owl:NamedIndividual>
<owl:NamedIndividual rdf:about="salaryRange2">
<location rdf:resource="location2"/>
<role rdf:resource="role2"/>
</owl:NamedIndividual>
With these individuals a reasoner like Hermit will infer that salaryRange1 and salaryRange2 are the same individual. However, if you state that salaryRange1 and salaryRange2 are different individuals, you will get an inconsistency.
<rdf:Description>
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDifferent"/>
<owl:distinctMembers rdf:parseType="Collection">
<rdf:Description rdf:about="salaryRange1"/>
<rdf:Description rdf:about="salaryRange2"/>
</owl:distinctMembers>
</rdf:Description>
To resolve the inconsistency you can state that either role1 and role2 are different individuals OR location1 and location2 are different individuals.
I am using the OWL-API 5 to load all the object property axioms in my ontology as follows:
File ontology = new File("examples/ontology.owl");
File individual = new File("examples/individuals.owl");
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
IRI documentIRI = IRI.create(ontology);
IRI ontologyIRI = IRI.create("http://www.semanticweb.org/2020/0/test");
SimpleIRIMapper mapper = new SimpleIRIMapper(ontologyIRI, documentIRI);
manager.getIRIMappers().add(mapper);
OWLOntology kb = manager.loadOntologyFromOntologyDocument(individual);
Stream<OWLObjectPropertyAssertionAxiom> objectPropertyAxioms = kb.axioms(AxiomType.OBJECT_PROPERTY_ASSERTION);
objectPropertyAxioms.forEach(axiom -> {
System.out.println("Found object property axiom " + axiom);
OWLIndividual object = axiom.getObject();
OWLIndividual subject = axiom.getSubject();
OWLObjectPropertyExpression property = axiom.getProperty();
});
Returns:
Found object property axiom ObjectPropertyAssertion(<http://www.semanticweb.org/2020/0/test#Q> <http://www.semanticweb.org/2020/0/test#x> <http://www.semanticweb.org/2020/0/test#y>)
Now, I'd like to determine if the property is functional. This is what I tried so far:
if (EntitySearcher.isFunctional(property, kb)) {
LOGGER.debug("Property " + property + " is declared as functional");
} else {
LOGGER.debug("Property " + property + " is NOT declared as functional");
}
Returns:
Property <http://www.semanticweb.org/2020/0/test#Q> is NOT declared as functional
I think that EntitySearcher.isFunctional(p,o) is looking for functional object property axioms that make the specified object property functional, which doesn't seems to exist in my ontology (i.e., ontology.axioms(AxiomType.FUNCTIONAL_OBJECT_PROPERTY) returns nothing).
This is what I have in my ontology:
<?xml version="1.0"?>
<rdf:RDF xmlns="http://www.semanticweb.org/2020/0/test#" xml:base="http://www.semanticweb.org/2020/0/test" xmlns:owl="http://www.w3.org/2002/07/owl#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<owl:Ontology rdf:about="http://www.semanticweb.org/2020/0/test"/>
<owl:ObjectProperty rdf:about="http://www.semanticweb.org/2020/0/test#Q">
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
</owl:ObjectProperty>
</rdf:RDF>
And individuals:
<?xml version="1.0"?>
<rdf:RDF xmlns="http://www.semanticweb.org/2020/0/test#" xmlns:owl="http://www.w3.org/2002/07/owl#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<owl:Ontology rdf:about="">
<owl:imports rdf:resource="http://www.semanticweb.org/2020/0/test"/>
</owl:Ontology>
<owl:NamedIndividual rdf:about="http://www.semanticweb.org/2020/0/test#x">
<Q rdf:resource="http://www.semanticweb.org/2020/0/test#y"/>
</owl:NamedIndividual>
<owl:NamedIndividual rdf:about="http://www.semanticweb.org/2020/0/test#y"/>
</rdf:RDF>
(Both files were created using Prótegé 5.5.0). Any suggestions? Thank you.
To answer the implicit question about EntitySearcher::isFunctional, yes, it checks if there are functional property axioms for the property received in input.
I believe your ontology has enough information for that.
To provide a complete example:
<?xml version="1.0"?>
<rdf:RDF xmlns="http://www.semanticweb.org/2020/0/test#" xml:base="http://www.semanticweb.org/2020/0/test" xmlns:owl="http://www.w3.org/2002/07/owl#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:test="http://www.semanticweb.org/owlapi/test#">
<owl:Ontology rdf:about="http://www.semanticweb.org/2020/0/test"/>
<owl:ObjectProperty rdf:about="http://www.semanticweb.org/owlapi/test#Q">
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
</owl:ObjectProperty>
<owl:NamedIndividual rdf:about="http://www.semanticweb.org/owlapi/test#x">
<test:Q rdf:resource="http://www.semanticweb.org/owlapi/test#y"/>
</owl:NamedIndividual>
<owl:NamedIndividual rdf:about="http://www.semanticweb.org/owlapi/test#y"/>
</rdf:RDF>
With this ontology, EntitySearcher returns true when checking for functional properties.
Edit: in your updated question, you're using kb.axioms(AxiomType), you need to tell it to include imports.
In the food ontology http://www.w3.org/TR/2004/REC-owl-guide-20040210/food#, I am seeing this way of representing classes and individuals:
<owl:Class rdf:ID="RedMeat">
<rdfs:subClassOf rdf:resource="#Meat"/></owl:Class>
<SweetDessert rdf:ID="Cake"/>
<SweetFruit rdf:ID="Bananas"/>
<SweetFruit rdf:ID="MixedFruit"/>
For the individuals "Cake", "Bananas" and "MixedFruit", why doesn't it use:
<OWL:NamedIndividual>
</OWL:NamedIndividual>
Is that because the food ontology is represented in old syntax?
I tried working with the OWL2-RL rules build into graphdb. I am obviously doing something wrong or understood something wrong. Here is my toy ontology.
<?xml version="1.0"?>
<rdf:RDF xmlns="http://www.semanticweb.org/rlehmann/ontologies/2017/10/untitled-ontology-182#"
xml:base="http://www.semanticweb.org/rlehmann/ontologies/2017/10/untitled-ontology-182"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:untitled-ontology-182="http://www.semanticweb.org/rlehmann/ontologies/2017/10/untitled-ontology-182#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:ontology="http://www.some/ontology/">
<owl:Ontology rdf:about="http://www.semanticweb.org/rlehmann/ontologies/2017/10/untitled-ontology-182"/>
<!-- http://www.some/ontology/hasValue -->
<owl:DatatypeProperty rdf:about="http://www.some/ontology/hasValue">
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#string"/>
</owl:DatatypeProperty>
<!-- http://www.some/ontology/ClassA -->
<owl:Class rdf:about="http://www.some/ontology/ClassA"/>
<!-- http://www.some/ontology/InvA -->
<owl:NamedIndividual rdf:about="http://www.some/ontology/InvA">
<rdf:type rdf:resource="http://www.some/ontology/ClassA"/>
<ontology:hasValue rdf:datatype="http://www.w3.org/2001/XMLSchema#string">StringValue</ontology:hasValue>
</owl:NamedIndividual>
<!-- http://www.some/ontology/InvB -->
<owl:NamedIndividual rdf:about="http://www.some/ontology/InvB">
<ontology:hasValue rdf:datatype="http://www.w3.org/2001/XMLSchema#string">FooBar</ontology:hasValue>
</owl:NamedIndividual>
<owl:Restriction>
<owl:onProperty rdf:resource="http://www.some/ontology/hasValue"/>
<owl:someValuesFrom rdf:resource="http://www.w3.org/2001/XMLSchema#string"/>
<rdfs:subClassOf rdf:resource="http://www.some/ontology/ClassA"/>
</owl:Restriction>
</rdf:RDF>
If I did not misunderstand Table 2 in OWL2-Profiles this ontology should be in OWL2 RL profile. I would expect "InvB" to be classified as type ClassA. But it doesn't. It actually does with reasoners (HermiT, Pellet, ...) but not with Rules (Drools, Graphdb) is this a gap in the specification.
How can my Ontology be "repaired" or is there any workaround?
Cheers,
Robert
Not using anonymous classes on the left-hand side of the GCI is not an option for our application. And yes object properties work perfectly.
After some research we actually found out, that it can never work that way. In the GraphDB .pie file that corresponds to the RL-Profile there is only a notion to some rules "// These are not implemented (and probably not implementable)". This includes the rule "dt-type2" defined in OWL2 RL Section 4.3 Table 8. RDF section 3.1 gives the actual answer why this is not supposed to work.
RDF Graphs
An RDF graph is a set of RDF triples.
3.1 Triples
An RDF triple consists of three components:
the subject, which is an IRI or a blank node
the predicate, which is an IRI
the object, which is an IRI, a literal or a blank node
"FooBar"^^xsd:string rdf:type xsd:string this is simply not allowed but obviously required.
We are quite uncertain what the guys at w3c had in mind besides RDF?!
As it is now, this kind of inference will not work in GraphDB at all (and on no rule engines in general?). But it is not the fault of GraphDB but merely a gap in the specification chain.
However, we did a workaround in our ontology that solves the problem and is working for us. We simply
defined new concepts for the data types we use
Transformed all DataProperties to ObjectProperties
introduces new DataType properties with domain one of the new datatype concepts and range xsd:xyz. For example Property:hasStringValue Domain:string Range:xsd:string
This works for us.
I am currently building/modifying a larger ontology. As I had problems to define restrictions I build a very short example:
I have EuropeanCountry as a class and IslandCountry as a class:
<owl:Class rdf:about="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#EuropeanCountry">
<rdfs:subClassOf rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#Country"/>
</owl:Class>
<!-- http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#IslandCountry -->
<owl:Class rdf:about="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#IslandCountry">
<rdfs:subClassOf rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#Country"/>
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#borders"/>
<owl:maxQualifiedCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">0</owl:maxQualifiedCardinality>
<owl:onClass rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#EuropeanCountry"/>
</owl:Restriction>
</rdfs:subClassOf>
</owl:Class>
As you can see I set a "maxQualifiedCardinality" restriction in Protege. If I create some individuals and (C1, C2 and Germany are EuropeanCountry, Island is IslandCountry) and relate them with the border property:
<owl:NamedIndividual rdf:about="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#Island">
<rdf:type rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#IslandCountry"/>
<borders rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#C1"/>
<borders rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#C2"/>
<borders rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#Germany"/>
</owl:NamedIndividual>
I get an error thrown by Hermit reasoner that it is not allowed to set 3 neighbours to Island. If I now change the line
<owl:maxQualifiedCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxQualifiedCardinality>
to cardinality 1 I don't get any error if I set three neighbours as shown in the example.
Can anyone explain this and hopefully provide me a solution how I can write a restriction that one class should have x other classes (in this case how to write that Island should have 2 neighbours and a third one will throw an error by the reasoner)?
Thanks for your help and kind regards,
tanktoo
Edit:
I have now added all individuals to an AllDifferent:
<rdf:Description>
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#AllDifferent"/>
<owl:distinctMembers rdf:parseType="Collection">
<rdf:Description rdf:about="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#C1"/>
<rdf:Description rdf:about="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#C2"/>
<rdf:Description rdf:about="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#Germany"/>
</owl:distinctMembers>
</rdf:Description>
It is now working with the restriction above and the reasoner tells me that I am not allowed to set 3 border countries as maxCardinality is 1.
I have now changed my restriction to the following:
<owl:Class rdf:about="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#IslandCountry">
<rdfs:subClassOf rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#Country"/>
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#borders"/>
<owl:minQualifiedCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minQualifiedCardinality>
<owl:onClass rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#EuropeanCountry"/>
</owl:Restriction>
</rdfs:subClassOf>
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#borders"/>
<owl:maxQualifiedCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">2</owl:maxQualifiedCardinality>
<owl:onClass rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#EuropeanCountry"/>
</owl:Restriction>
</rdfs:subClassOf>
</owl:Class>
I would now expect that the reasoner detects an error if I set less than 1 or more than 2 neighbours:
<owl:NamedIndividual rdf:about="http://www.semanticweb.org/tanktoo
/ontologies/2016/10/untitled-ontology-81#Island">
<rdf:type rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#IslandCountry"/>
<borders rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#C1"/>
<borders rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#C2"/>
<borders rdf:resource="http://www.semanticweb.org/tanktoo/ontologies/2016/10/untitled-ontology-81#Germany"/>
</owl:NamedIndividual>
In this case the reasoner detects an errro because there are 3 neighbours. If I now delete all the neighbours so that Island border 0 countries the reasoner doesn't give me an error. Can someone explain me why?
Thanks for your help :)
I get an error thrown by Hermit reasoner that it is not allowed to set 3 neighbours to Island
If this is what the tool says, then it is a mistake. Granted, you are giving 3 names of bordering countries. But nothing says that these are names of 3 different countries. They could be several names of the same country, like "France", "Republic of France", "République française".
Since the reasoner has no way of knowing whether they are names of the same thing or not, it can't detect an inconsistency in the second case. However, in the first case, having at least a name for something means that there is strictly more than zero thing, so it makes sense that an inconsistency is detected.
If you'd like to make sure the reasoner detects when a country has more than 2 neighbours, then you'd have to explicitly say that the countries are different:
ex:C1 owl:differentFrom ex:C2, ex:Germany .
ex:C2 owl:differentFrom ex:Germany .
or:
[] a owl:AllDifferent;
owl:members ( ex:C1 ex:C2 ex:Germany ) .
In Protégé, in the Individual tab, you can specify from what an individual is different.