Replies: 13 comments 13 replies
-
Can't you just use a special object which is used for the GPR calculation which is based on a Gene? class GPRGene:
def __init__(self, gene: Gene):
self.name = gene.id
self.gene = gene You would use this for all the GPR parsing. I don't think it is a good idea to use aliases. People expect the name to be a certain thing. To change the complete code base because some library expects something is a bad idea. You have to create an abstraction layer as suggested above. |
Beta Was this translation helpful? Give feedback.
-
Hmmm, that is unfortunate but not much we can do. What Matthias suggested is in line with what the rest of cobrapy is doing in with optlang variables already (which are sympy/symengine symbols as well). So that makes sense. I would call it Optlang symbols can already use boolean expressions, but |
Beta Was this translation helpful? Give feedback.
-
Okay, I think I got it. When would sympify be used? I think the same logic could be applied to symengine when visiting the AST tree, just calling optlang.or instead of sympy.or. I could use optlang if optlang were to call logical_or, logical_and. It looks like symengine refers to sympy.core.S, so either using sympy or symengine, I can keep using that. I'm just not sure how to modify optlang. I would have to rewrite the reaction functional system, which is doable. Does this make sense? |
Beta Was this translation helpful? Give feedback.
-
Found some time to run a simple benchmark and it looks pretty bad 😢
Basically building the expression (even without parsing) is 10x slower with sympy and evaluating is about 1000x (!) slower than what we have now. This would add quite some time to parsing and a massive chunk of time to gene deletions. So for now I'm not sure how much sense it makes to have this especially since there seem to be no plans to add it to the symengine Python interface. So this is probably not in a usable state now. Maybe as an alternative we could start to save the AST as the GPR and not the string representation. |
Beta Was this translation helpful? Give feedback.
-
Some comments
What would/should eval_gpr(rxn1, list()) give me? In general, I think that sympy might be a bit slower, but if we only do the conversion once, it should not be very slow. Right now, we convert from str to AST and eval the AST multiple times. I'll time it when I'm ready. |
Beta Was this translation helpful? Give feedback.
-
Okay.
Shift all other modules to use this property. I think delete compiles the string AST, which would not be necessary for example. SBML io parses AST to string and back again, which could probably be simplified.
If cobrapy does have this, where is it defined? I've looked at the code and haven't found it |
Beta Was this translation helpful? Give feedback.
-
import ast
# ...
class GPR(ast.AST):
@staticmethod
def from_string(string_gpr):
# ...
@staticmethod
def from_sbml(sbml_exp):
# ...
def eval(self):
# ...
def __str__(self):
# ...
def __repr_html__(self):
# ...
def as_symbolic(self):
# ...
|
Beta Was this translation helpful? Give feedback.
-
I've set it up so reaction.gene_reaction_rule is basically the string version of GPR, and GPR is based on AST. reaction.gene_reaction_rule is no longer an entity by itself |
Beta Was this translation helpful? Give feedback.
-
You mean that
I think it should throw an error with a clear statement that it is not a valid GPR. Since the GPR is now an object its default value should switch to Feel free to split up PRs into smaller parts. For instance, by starting with a smaller implementation that only gets to feature parity with the current implementation and another one that adds the new features. This makes it easier to review. |
Beta Was this translation helpful? Give feedback.
-
Reaction._gene_reaction_rule is effectively which is I would have like to split the PR to parts, where the first one adds GPR to cobra.core.gene and doesn't change almost anything (although some test might fail) and the second part removes all irrelevant functions so ast2str, parse_gpr, eval_gpr become internal functions. However, I already pushed some of the extra commits to Github. How do I fix that, so the pull request won't be ALL the commits? |
Beta Was this translation helpful? Give feedback.
-
I don't think you need
I would structure the first PR so that it does pass all existing tests and moves the functions into the GPR class. So basically the same API but using the new GPR class in the background. If you have more stuff added and not pushed to your github repo you can just revert the commit with EDIT: sorry, misread a part :) |
Beta Was this translation helpful? Give feedback.
-
I made GPR a class, and gave it its own pull request. Please review This is exactly like Midnighter says, to keep compatibility. I'm going to keep working on it while it is reviewed to shift all the code to use the new class. |
Beta Was this translation helpful? Give feedback.
-
I'm closing this discussion due to the work by @akaviaLab that has been merged #1134. |
Beta Was this translation helpful? Give feedback.
-
This is a follow-up of the discussion on #1013
To summarize
One problem I've run into is the fact that sympy uses name to identify Symbols (like cobrapy uses id).
I can't stop sympy or optlang from using name without rewriting most of Sympy, which is not an option. Sympy will not recognize id, since many functions look to *.name
Currently, the Gene.name is the longer string that is potentially human readable, but with the sympy changes Gene.name will be equivalent to id.
I'm thinking of adding a field called "alias" to Gene, which will have the role of the current name (a longer/different str). I will then refactor all the internal functions that use Gene.name to use Gene.alias.
Would that work fine?
If it does, should Gene be created with name as a parameter, even if it will be internally renamed alias?
Something like this to keep consistency with previous usages of Gene
class Gene(Symbol, Species):
def init(self, id=None, name="", functional=True):
super().init(name=id)
super(Species, self)
self.alias = name
self.id = self.name
Or should it become def init(self, name=None, alias="", functional=True)
What about functions like (in Reaction)
@Property
def gene_name_reaction_rule(self):
"""Display gene_reaction_rule with names intead.
I think one option is to keep it, have it call gene_alias_reaction(self) and maybe print out a warning it will be deprecated. Or I can just keep it as is, and refer internally to alias.
If not, do you guys have suggestions for what would work?
One additional topic to think about is refactoring all of cobrapy to use alias (or whatever we decide). I think that might be overkill, but I'm opening to hearing what you say.
Thank you
Uri David
Beta Was this translation helpful? Give feedback.
All reactions