===================== Singular Annotations ===================== There's a little more to creating new annotations compared to reading and writing them. There are a few additional classes that need to be introduced: `Editor`, `SingleAnnotation`, `PersonalInformation`, `PhysicalEntity`, `PhysicalProcess` and `EnergyDiff`. This document assumes background knowledge in what these different types of annotations are. To demonstrate libOmexMeta we first generate a sbml document using tellurium and antimony. .. code-block:: python import tellurium as te ant = """ model ToyModel compartment cytosol = 1.0; A in cytosol; B in cytosol A = 10; B = 0; k1 = 0.1; k2 = 0.1; r1: A => B; k1*A r1: B => A; k2*B end""" sbml = te.antimonyToSBML(ant) print(sbml) Which produces: .. code-block:: xml k2 B Creating Single Annotations =========================== A SingularAnnotation object is a typedef'd `omexmeta::Triple` object which is essentially a wrapper around the `librdf_statement*` object from the Redland Libraries. In this example we create a simple singular annotation for the top level `model` element of the sbml. We also add this annotation to our RDF graph and serialize to Turtle syntax. Simple example of creating a single annotation ---------------------------------------------- .. tabs:: .. tab:: python .. literalinclude:: create_single_annotation_python.py :language: python :caption: Create a single annotation in Python .. literalinclude:: ../../ExampleOutputFiles/create_single_annotation_python.txt :caption: Output .. tab:: C++ .. literalinclude:: create_single_annotation_cpp.CPP :language: C++ :caption: Create a single annotation in C++ .. literalinclude:: ../../ExampleOutputFiles/CreateSingleAnnotationCpp.txt :caption: Output .. tab:: C .. literalinclude:: create_single_annotation_c.CPP :language: C :caption: Create a single annotation in C Output: .. literalinclude:: ../../ExampleOutputFiles/CreateSingleAnnotationC.txt :caption: Output The first thing to notice is that the sbml string only has a single metaid called `ToyModel`, which is the metaid of the model element. Metaids are essential for annotation, given that you cannot annotate an element that does not have a metaid. Using the `generate_new_metaids` argument ----------------------------------------- If only some of your model elements have metaids, you can use the third argument to `toEditor` to automatically create metaids .. tabs:: .. tab:: python .. literalinclude:: create_single_annotation_with_metaids_python.py :caption: Create a single annotation in Python and create metaids .. literalinclude:: ../../ExampleOutputFiles/create_single_annotation_with_metaids_python.txt :caption: Output .. tab:: C++ .. literalinclude:: create_single_annotation_with_metaids_cpp.cpp :language: C++ :caption: Create a single annotation in C++ and create metaids .. literalinclude:: ../../ExampleOutputFiles/CreateSingleAnnotationWithMetaidsCpp.txt :caption: Output .. tab:: C .. literalinclude:: create_single_annotation_with_metaids_c.cpp :language: C :caption: Create a single annotation in C and create metaids .. literalinclude:: ../../ExampleOutputFiles/CreateSingleAnnotationWithMetaidsC.txt :caption: Output Creating and Serializing a Single Annotation without adding to the model ------------------------------------------------------------------------ In the previous examples, we added the singular annotation we had just constructed to the RDF graph and then serialized the RDF graph to turtle syntax. It is also possible to serialize a singular annotation (a typedef'd Triple) and a set of annotations (a Triples class) without adding the annotations to the RDF graph. .. tabs:: .. tab:: python .. literalinclude:: create_single_annotation_no_commit_python.py :language: python :caption: Create a single annotation in Python and serialize to turtle without commiting the triples to the current RDF graph .. literalinclude:: ../../ExampleOutputFiles/create_single_annotation_no_commit_python.txt :caption: Output .. tab:: C++ .. literalinclude:: create_single_annotation_no_commit_cpp.CPP :language: C++ :caption: Create a single annotation in C++ and serialize to turtle without commiting the triples to the current RDF graph .. literalinclude:: ../../ExampleOutputFiles/CreateSingleAnnotationNoCommitCpp.txt :caption: Output .. tab:: C .. literalinclude:: create_single_annotation_no_commit_c.cpp :language: C :caption: Create a single annotation in C and serialize to turtle without commiting the triples to the current RDF graph .. literalinclude:: ../../ExampleOutputFiles/CreateSingleAnnotationNoCommitC.txt :caption: Output Subjects, Predicates and Resources ================================== Singular annotations are Triples or RDF statements. Therefore by definition they are composed of three nodes (or terms): the subject, the predicate and the resource. For a more detailed description of what these are, please refer to the `RDF 1.1: Concepts and Abstract Syntax document `_. The RDF is a very general framework for storing information. In libOmexMeta we have restricted this generality in accordance with the OmexMeta specification 1.1. Subject --------- A subject in libOmexMeta is the metaid of the element being element appended onto the local namespace. In RDF, the subject is the value of the `about` attribute. To set the subject of an annotation in libOmexMeta, use the `about()` method. Although we focus on singular annotations here, this generalizes to the other of annotations supported by libOmexMeta. In this example, we have forced the generation of new metaids, which defaults to `##OmexMetaId` followed by a 4 digit number. Hence the metaid `#OmexMetaId0002` exists and is the metaid for the `Species` element with the name `"A"` (see xml above). .. warning:: The following example is not a complete example. It will fail because a SingularAnnotation is only valid when all three of subject, predicate and resource are specified. For brevity, we assume `sbml` is the same as in the above examples. .. tabs: .. tab: Python .. code-block:: Python :caption: Set the subject portion of the RDF triple in Python. rdf = RDF() with rdf.to_editor(sbml, generate_new_metaids=True) as editor: with editor.new_singular_annotation() as singular_annotation: singular_annotation.about('#OmexMetaId0002') .. code-block:: C++ :caption: Set the subject portion of the RDF triple in C++. RDF rdf; Editor editor = rdf.toEditor(sbml, "sbml", false); SingularAnnotation singular_annotation = editor.newSingularAnnotation(); singular_annotation.about("species0000") .. code-block:: C :caption: Set the subject portion of the RDF triple in C. // create an empty RDF object RDF *rdf_ptr = RDF_new(); // create editor object Editor *editor_ptr = RDF_toEditor(rdf_ptr, sbml, true, true); // create out annotation SingularAnnotation *singular_annotation = Editor_newSingularAnnotation(editor_ptr); SingularAnnotation_about(singular_annotation, "species0000"); However, leaving `generate_new_metaids=False` (the default) causes an error, because the metaid `#OmexMetaId0002` was not found in your model. .. tabs:: .. tab:: Python .. code-block:: Python :caption: Raise error whilst setting the subject portion of the RDF triple in Python. rdf = RDF() with rdf.to_editor(sbml, generate_new_metaids=True) as editor: with editor.new_singular_annotation() as singular_annotation: singular_annotation.about('#OmexMetaId0002') .. tab:: C++ .. code-block:: C++ :caption: Raise error whilst setting the subject portion of the RDF triple in C++. RDF rdf; Editor editor = rdf.toEditor(sbml, "sbml", false); SingularAnnotation singular_annotation = editor.newSingularAnnotation(); singular_annotation.about("species0000") .. tab:: C .. code-block:: C :caption: Raise error whilst setting the subject portion of the RDF triple in C. // create an empty RDF object RDF *rdf_ptr = RDF_new(); // create editor object Editor *editor_ptr = RDF_toEditor(rdf_ptr, sbml, OMEXMETA_TYPE_SBML, false); // create out annotation SingularAnnotation *singular_annotation = Editor_newSingularAnnotation(editor_ptr); SingularAnnotation_about(singular_annotation, "species0000"); Predicate --------- Predicates in libOmexMeta are all subclasses of the Predicate class. Existing predicate classes include `BiomodelsBiologyQualifier`, `BiomodelsBiologyQualifier`, `DCTerm`, `SemSim` and `Foaf`. For convenience, these predicate classes are only ever instantiated using a Factory method, which takes two string arguments: the namespace and the term, i.e: .. list-table:: Build in namespaces and prefixes for Predicates. :widths: 25 25 :header-rows: 1 * - Namespace - Prefix * - http://xmlns.cppom/foaf/0.1/ - foaf * - http://purl.org/dc/terms/ - dc * - http://biomodels.net/model-qualifiers/" - bqmodel * - http://biomodels.net/biology-qualifiers/" - bqbiol * - http://www.bhi.washington.edu/semsim#" - semsim Each of these namespaces are referred to by their prefix. The term portion of the predicates are by defined by the namespace you are using and can easily be googled. An alternative strategy however, is simply to "get it wrong" and run your program and you will be provided with a full list of available terms for the prefix you are using. .. warning:: The following example will fail because the singular annotations built are not complete. .. tabs:: .. tab:: Python .. code-block:: Python :caption: Setting the Predicate portion of a singular annotation in Python rdf = RDF() with rdf.to_editor(sbml, generate_new_metaids=True) as editor: # build an annotation with bqbiol predicate with editor.new_singular_annotation() as bqbiol_singular_annotation: bqbiol_singular_annotation.set_predicate('bqbiol', 'is') # build an annotation with bqmodel predicate with editor.new_singular_annotation() as bqmodel_singular_annotation: bqmodel_singular_annotation.set_predicate('bqmodel', 'isDerivedFrom') # build an annotation with dcterm predicate with editor.new_singular_annotation() as dc_singular_annotation: dc_singular_annotation.set_predicate('dc', 'date') .. code-block:: C++ :caption: Setting the Predicate portion of a singular annotation in C++ RDF rdf; Editor editor = rdf.toEditor(sbml, "sbml", false); SingularAnnotation bqbiol_singular_annotation = editor.newSingularAnnotation(); bqbiol_singular_annotation.setPredicate("bqbiol", "is") SingularAnnotation bqmodel_singular_annotation = editor.newSingularAnnotation(); bqmodel_singular_annotation.setPredicate("bqmodel", "isDerivedFrom") SingularAnnotation dc_singular_annotation = editor.newSingularAnnotation(); dc_singular_annotation.setPredicate("dc", "date") .. code-block:: C :caption: Setting the Predicate portion of a singular annotation in C // create an empty RDF object RDF *rdf_ptr = RDF_new(); // create editor object Editor *editor_ptr = RDF_toEditor(rdf_ptr, sbml, OMEXMETA_TYPE_SBML, false); // create out annotation SingularAnnotation *bqbiol_singular_annotation = Editor_newSingularAnnotation(editor_ptr); SingularAnnotation_setPredicate(singular_annotation, "bqbiol", "is); SingularAnnotation *bqmodel_singular_annotation = Editor_newSingularAnnotation(editor_ptr); SingularAnnotation_setPredicate(singular_annotation, "bqmodel", "isDerivedFrom); SingularAnnotation *bqbiol_singular_annotation = Editor_newSingularAnnotation(editor_ptr); SingularAnnotation_setPredicate(singular_annotation, "dc", "date"); It should be noted that these Predicate subclasses in libOmexMeta are only convenience structures to ease the collection of information from the user. It is also possible and sometimes necessary to manually give libOmexMeta the entire predicate string. In C++, the setPredicate is overloaded to accept a single string, which indicates that this is a full uri. In C and Python, this is a different method call - see below. .. tabs:: .. tab:: python .. literalinclude:: create_single_annotation_predicate_from_uri_python.py :language: python :caption: Create a single annotation in Python using an arbitrary uri .. literalinclude:: ../../ExampleOutputFiles/create_single_annotation_predicate_from_uri_python.txt :caption: Output .. tab:: C++ .. literalinclude:: create_single_annotation_predicate_from_uri_cpp.cpp :language: C++ :caption: Create a single annotation in C++ using an arbitrary uri .. literalinclude:: ../../ExampleOutputFiles/CreateSingleAnnotationPredicateFromUriCpp.txt :caption: Output .. tab:: C .. literalinclude:: create_single_annotation_predicate_from_uri_c.cpp :language: C :caption: Create a single annotation in C using an arbitrary uri .. literalinclude:: ../../ExampleOutputFiles/CreateSingleAnnotationPredicateFromUriC.txt :caption: Output Resources ---------- A resource node can be any of the three types of node: a literal, uri or a blank node. Separate methods exist for each of these. In the next example we create a set of singular annotations that demonstrate the various usages of Resource. .. tabs:: .. tab:: Python .. literalinclude:: create_single_annotation_resource_python.py :language: python :caption: Demonstration of options for the resource node in Python .. literalinclude:: ../../ExampleOutputFiles/create_single_annotation_resource_python.txt :caption: Output .. tab:: C++ .. literalinclude:: create_single_annotation_resource_cpp.cpp :language: c++ :caption: Demonstration of options for the resource node in C++ .. literalinclude:: ../../ExampleOutputFiles/CreateSingleAnnotationResourceCpp.txt :caption: Output .. tab:: C .. literalinclude:: create_single_annotation_resource_c.cpp :language: C :caption: Demonstration of options for the resource node in C .. literalinclude:: ../../ExampleOutputFiles/CreateSingleAnnotationResourceC.txt :caption: Output