Recipe to Cake
At HARMONY in New York, we reached consensus on a design decision for the comp package: the package would be rule-based and not object-based. In a metaphor for this process, it is like the comp model file is a recipe, but not an actual cake.
This is all well and good for the model exchange part, but what happens when you want to actually use the model the rules describe? How, in other words, do you follow the recipe and bake the cake?
First, we define the end state of what the object model will look like. It will no longer have an equivalent XML form. (XML is a common vehicle for SBML, but not what SBML is 'about'.) There will be a single object present for every SBML element in the final model. It will not have objects present which are not in the final model. All references to elements in the model will point to the correct object. And any mathematical processing due to the presence or absence of conversion factors will have been followed.
Note: this last may be controversial. Is it actually necessary to process the conversion factor rules every time you want an one-to-one memory model of the SBML model? My guess is yes. Discuss ;-)
So, what are the rules we need to follow? There are three:
- Copying The 'Submodel' element is a copy rule. "Take this model and create a local copy of it."
- Removing The 'Deletion' element is a removal rule. "Remove this part of the model you just copied."
- Synchronizing and Conversion The 'ReplacedElement' element is a more sophisticated 'removal' rule: "Remove this part of the model you just copied, but take any references to that part and point them instead to this new element. In any mathematical context, multiply that reference by this conversion factor."
So, one needs routines to perform these three manipulations. The manipulations themselves are comp-agnostic: anyone editing a model may wish to perform those procedures. Therefore, to start down the pragmatic 'how do we do this, then?' road, it might make sense to include routines to perform these changes in libsbml itself.
Copying and removal are already present. What is not present is a way to easily find the element you want to copy or remove. Since the target of a 'copy' action is always 'a model', and multiple models only exist in the comp package, the function 'get me the model with this id' belongs in the comp package. But for the removal rules, it would be nice to have a general function 'get me the element with this id'. This was already planned to be added to libsbml (and has obvious uses outside of comp), so once this is in place, we're 2/3 rules away from having all the tools we need.
The third rule is more complicated, however. First, we need to find all the SIdRefs and IDREFs (aka metaIDRefs) that point to a replaced element in the model, and change them to an SIdRef or IDREF for the replacement object. This is greatly complicated by the fact that the replacement object is in a different model altogether! Libsbml can't help with the latter, but it could help with the former: you could tell it 'I have changed this SId to this other SId' it could recurse through its elements and change all SIdRefs that matched the first SId to be equal to the new SId instead. You'd then have to do some work on your own to deal with the fact that the new SId is in a different model: perhaps creating a placeholder object with the new SId in the submodel, along with a custom annotation that indicated where the 'real' object was. Or if your object model relied on actual pointers to objects instead of using strings, you could simply redirect the pointers to point to the correct objects.
Finally, you need a way to deal with the conversion factors. As discussed in section 3.6 in the proposed specification, some conversion factors are relatively complicated formulas. If one wished to incorporate these factors into the actual math elements of various SBML constructs, the simplest method would be for it to provide us with a routine that would substitute a ci element with an ASTNode root. This routine could be merged with the above renaming routine as well--perhaps something like:
SBase::renameSId(string origname, string newname, ASTNode* conversionFactor)
Any SBase object that contained a 'math' subelement would then search its ASTNode for a <ci> element with the name 'origname', and replace it with a 'times' ASTNode with two children: a ci element with the name 'newname', and the provided conversionFactor. (If the conversionFactor was null, only the name would need to be changed.)
So, with the caveat that libsbml would not be able to provide you with a way to have SIdRefs point to an element outside of the model that element was a member of (since no such concept exists in core), you would have all the tools you needed to 'bake the cake'.
Interestingly, the caveat is no longer needed if one wishes to actually completely flatten the model to no longer use 'comp' constructs. But that's a topic for another page.