SynBioDex / sbol_factory

The SBOLFactory provides a mechanism for automatically generating an interactive, object-oriented API from a declarative data model specification encoded in OWL.
MIT License
6 stars 3 forks source link

Document OWL sublanguage supported by the sbol_factory #5

Open rpgoldman opened 3 years ago

rpgoldman commented 3 years ago

Discussing experiences with other users, it seems that one needs to know what OWL constructs are and are not supported in order to effectively use the sbol_factory.

As I am working on the Containers ontology for PAML, I feel vulnerable to wasting time by encoding constraints that will either be ignored by the sbol_factory or that might cause it to malfunction.

Please expand the docs to explain what patterns to avoid, etc.

bbartley commented 3 years ago

There is a very limited subset of OWL that is currently supported. Most of the supported patterns are in the following example:

opil:ParameterValue rdf:type owl:Class ;
                    rdfs:subClassOf sbol:Identified  # The ontology must "hook in" to SBOL through a subclass relationship. If this relationship isn't defined, the factory can't generate an API class

opil:hasMeasure rdf:type owl:ObjectProperty ;
                rdfs:subPropertyOf opil:compositionalProperty ;  # This is used for constructing parent-child data structures
                rdfs:domain opil:MeasureValue ;
                rdfs:range om:Measure ;
                rdfs:label "has measure" .  # The label is used to generate the Python attribute name, in this case has_measure

opil:MeasureValue rdf:type owl:Class ;
                  rdfs:subClassOf opil:ParameterValue ,
                                  [ rdf:type owl:Restriction ;
                                    owl:onProperty opil:hasMeasure ;
                                    owl:minCardinality "1"^^xsd:nonNegativeInteger ;
                                    owl:maxCardinality "1"^^xsd:nonNegativeInteger   # If this isn't provided, SBOLfactory will assume the property should be assigned a list value
                                  ] ;
rpgoldman commented 3 years ago

@bbartley Thanks! That is really helpful. Some follow-up questions:

  1. What is done with subPropertyOf? I didn't understand what you mean by constructing parent-child data structures. Do you mean that you override the parent's property in the child class?
  2. You say that the label is used to generate the python attribute name. What happens if there is no rdfs:label property? Do you use the trailing part of the class IRI? Or is this an error case?
  3. What do you do in the following case: a class C has subclass C1 and C has property P. On objects of class C, the cardinality constraint is min cardinality 1. Class C1 adds max cardinality 1. At this point, if you make the P property of C1 be a singleton, and the P property of C be a list, things get very confusing. I was discussing this case with @jakebeal the other day: it's a reason never to represent an OWL property as a singleton-valued Python property. In the past, we have worked with a couple of libraries that tried to guess when a property could be a singleton, versus when they should be a collection value, and it has always caused problems. I strongly recommend not doing this.
rpgoldman commented 3 years ago

Actually, on reflection, my guess about question number one does not make sense, since there can be multiple disjoint sub-properties of a single OWL property. So I am afraid I don't understand how subProperty should work at all.

bbartley commented 3 years ago
  1. A compositionalProperty is a type of property that does not exist in the OWL meta-model, but does exist in SBOL and its extension languages (e.g., OPIL, PAML). See here: https://www.visual-paradigm.com/guide/uml-unified-modeling-language/uml-aggregation-vs-composition/. SBOL supports both aggregated as well as compositional data structures, whereas OWL only supports the former. Since we can't express compositional relationships natively using OWL, we invented this term opil:compositionalProperty
  2. It's an error case.
  3. I don't necessarily have a good response to this. In terms of implementation, it's expedient and it works. It may be abusing OWL semantics, however.
jakebeal commented 3 years ago

Note that compositionalProperty should get its namespace changed per #10

jakebeal commented 3 years ago

With regards to @rpgoldman 's point 3:

What do you do in the following case: a class C has subclass C1 and C has property P. On objects of class C, the cardinality constraint is min cardinality 1. Class C1 adds max cardinality 1. At this point, if you make the P property of C1 be a singleton, and the P property of C be a list, things get very confusing

Here, I would say that since C.P has list type, so must C1.P, since otherwise it would conflict with the definition in its superclass.

The principle for reasoning: restrictions can be tightened, but not relaxed. Thus, the list/singleton distinction always gets set by the parent class.

rpgoldman commented 3 years ago
1. A `compositionalProperty` is a type of property that does not exist in the OWL meta-model, but does exist in SBOL and its extension languages (e.g., OPIL, PAML).  See here: [visual-paradigm.com/guide/uml-unified-modeling-language/uml-aggregation-vs-composition](https://www.visual-paradigm.com/guide/uml-unified-modeling-language/uml-aggregation-vs-composition/).  SBOL supports both aggregated as well as compositional data structures, whereas OWL only supports the former.  Since we can't express compositional relationships natively using OWL, we invented this term opil:compositionalProperty

Oh, I see. So a compositionalProperty is a hasPart property rather than a different kind of property? So are you saying effectively that "anything that is a measure of an entity is also a part of that entity"? Because I'm pretty sure that is what rdfs:subPropertyOf means:

The property rdfs:subPropertyOf is an instance of rdf:Property that is used to state that all resources related by one property are also related by another.

A triple of the form:

P1 rdfs:subPropertyOf P2 

states that P1 is an instance of rdf:Property, P2 is an instance of rdf:Property and P1 is a subproperty of P2. The rdfs:subPropertyOf property is transitive.

Is that what you wanted? Just from reading the name 'compositionalPropertyit seems more like what you were trying to do was to say something special about the propertyhasMeasure` itself. If that is what you want, what would be better would be something like

:isCompositionalProperty rdf:type owl:AnnotationProperty .

opil:hasMeasure rdf:type owl:ObjectProperty ;
                :isCompositionalProperty xsd:True.

An owl:AnnotationProperty has whatever semantics you want it to have, instead of taking the rdfs:subPropertyOf semantics.

3. I don't necessarily have a good response to this.  In terms of implementation, it's expedient and it works. It may be abusing OWL semantics, however.

Well, I have been involved in two programs (the BBN-led Integrated Learning and the SIFT-led Oh By The Way), and in both of them it was a real problem to narrow properties down to singletons. Because of the fact that a class could have any number of values for property P, and a subclass could have exactly one, you can end up with a translator that gives you a method p that will return either a list or a single value depending on the object it is called on, and there is no easy way to paper over that potential mismatch. Most of the time you get a list, but if you happen to call p on subclass you get a single data structure instead.

Having lists all the time can be cumbersome, but the alternative gives you creeping type uncertainty.

rpgoldman commented 3 years ago

With regards to @rpgoldman 's point 3:

What do you do in the following case: a class C has subclass C1 and C has property P. On objects of class C, the cardinality constraint is min cardinality 1. Class C1 adds max cardinality 1. At this point, if you make the P property of C1 be a singleton, and the P property of C be a list, things get very confusing

Here, I would say that since C.P has list type, so must C1.P, since otherwise it would conflict with the definition in its superclass.

The principle for reasoning: restrictions can be tightened, but not relaxed. Thus, the list/singleton distinction always gets set by the parent class.

I think that's the correct reasoning, but I'm not sure that it's worth the additional complication in writing the translator, and maintaining code if you change your mind about cardinalities, versus just always having lists. But it's your call which is easier in the long run.

jakebeal commented 3 years ago

@rpgoldman On the matter of lists vs. singletons: based on past experience, many of our users would find it hateful to use lists to interact with singleton items. The "if you happen to call p on subclass you get a single data structure instead" failure mode shouldn't be able happen with the semantics proposed here.

I think the point about rdfs:subPropertyOf is good; @bbartley, do you want to change to the annotation model?

rpgoldman commented 3 years ago

@rpgoldman On the matter of lists vs. singletons: based on past experience, many of our users would find it hateful to use lists to interact with singleton items. The "if you happen to call p on subclass you get a single data structure instead" failure mode shouldn't be able happen with the semantics proposed here.

The semantics you propose work, I think, as long as you are willing to implement them in the compiler.

jakebeal commented 3 years ago

Suggested alternate solution from @bbartley that we're aiming for: change from using owl:ObjectProperty to using sbol:CompositionalObjectProjerty, which will be a subclass of owl:ObjectProperty.

Then we don't need to say anything about either subPropertyOf or an annotation.

rpgoldman commented 3 years ago

Suggested alternate solution from @bbartley that we're aiming for: change from using owl:ObjectProperty to using sbol:CompositionalObjectProjerty, which will be a subclass of owl:ObjectProperty.

Then we don't need to say anything about either subPropertyOf or an annotation.

That sounds right to me, but it’s above my pay grade where OWL is concerned.

Off-hand I’m surprised that this is allowed, since doesn’t subclassing property allow you second order expressive power (because you can quantify over the instances of sbol:CompositionalObjectProperty? These are deep logical waters, about which I am not informed.

jakebeal commented 3 years ago

Looks like subclassing ObjectProperty is both allowed and not allowed: https://stackoverflow.com/questions/62679433/defining-a-subclass-of-owlobjectproperty-with-domains-range-constraint

rpgoldman commented 3 years ago

I’m sorry, but I think this is a pretty strong argument for using an annotation instead. Annotations were made specifically for dealing with things ordinary OWL (which is really OWL DL) can’t. Breaking this sort of rule is likely to break standard OWL tools, so let’s not do that

jakebeal commented 3 years ago

Recommendation for resolving both this and https://github.com/SynBioDex/sbol-owl3/issues/1 from meeting of @bbartley , @jakebeal and @goksel :

Using a subproperty relationship for parent/child relationships. This, given e.g., A paml:node B you can also correctly conclude A [hasChild] B. This avoids the problems with trying to subclass object property. You can also trace these relationships in order to determine closure blocks if you want.

The name to be used for the property is still not determined. Possibilities include: