RuleWorld / bionetgen

Rule-based modeling framework
https://bionetgen.org/
MIT License
56 stars 25 forks source link

Better support for species synthesis in rules #58

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Currently, the products of synthesis reactions must be fully specified species. 
This convention is safe but is probably overly restrictive. There has been some 
discussion in the past of supporting default state values and copy constructors 
in synthesis rules.

For example, consider the molecule type M(a~0~1,b~0~1,c~0~1). Currently, 
synthesis rules must contain fully specified product species, such as

Null() -> Null() + M(a~0,b~1,c~1)  ksynth

It has been suggested that the first listed state values in the molecule type 
declaration (the 0's in this case) be considered default values, so that rules 
like

Null() -> Null() + M()  ksynth

can be written, which would be equivalent to

Null() -> Null() + M(a~0,b~0,c~0)  ksynth

(Note that all components would be unbound by default as well.) It has also 
been suggested that partial species specifications be allowed, i.e., default 
state values be used only for *unspecified* components. For example,

Null() -> Null() + M(a~1)  ksynth

would be equivalent to 

Null() -> Null() + M(a~1,b~0,c~0)  ksynth

Furthermore, the possibility of using a tagged species as a template for a 
synthesized species has been discussed. An example where this would be 
particularly useful is cell proliferation, where we might write a rule like

Cell() -> Cell() + Cell()  kdiv

Currently this is not allowed because of the requirement that products be fully 
specified species. Implementing the recommendations above would help to some 
extent in that this rule would be valid and would create a new Cell() object 
with default state values for all components. However, the more biophysically 
realistic situation is for the new Cell() object to be a copy of the original. 
This could be accomplished by tagging the reactant object and passing the tag 
to the new object on the product side to act as a template, as in:

Cell()%x -> Cell()%x + Cell(x)  kdiv

There has been some debate as to whether the tag should be passed as the bare 
variable 'x' or with a preceding symbol that identifies it explicitly as a tag 
(pointer), such as '&x' or perhaps '%x'. The advantage to using the bare 
variable is that it's consistent with how tags are currently passed to local 
functions (both in NFsim and BNG). The disadvantage is that it superficially 
looks like a component name. Practically speaking, this means that tags must 
occupy the same namespace as components so as to avoid overlap of variable 
names. Another option would be to use a special character, such as a semicolon 
or vertical bar, to distinguish the tag from the components, e.g., Cell(;x), 
Cell(x;), Cell(|x) or Cell(x|).

This last point is important because we would also like to allow overloading of 
specified components, similar to above, as in:

Cell()%x -> Cell()%x + Cell(x,a~0,b~1)  kdiv

Basically, the tagged species would provide the default state values rather 
than the molecule type declaration. These could be overridden by the user for 
any components that they desire. A practical example of where this might be 
useful is if a component were defined that tracked whether a Cell was newly 
created or not, e.g.:

Cell(s~old)%x -> Cell(s~new)%x + Cell(x,s~new)  kdiv

Presumably, additional rules would be specified that act exclusively on 'new' 
Cells. A rule would also be written that converts 'new' Cells into 'old' Cells.

Original issue reported on code.google.com by lh64@cornell.edu on 24 Sep 2012 at 10:00

GoogleCodeExporter commented 9 years ago
Note that molecules (agents) can be created in a Kappa rule without a complete 
specification of components and states (i.e., an incomplete interface). 
According to the KaSim3 manual 
(http://github.com/jkrivine/KaSim/blob/master/man/KaSim_manual.pdf): "KaSim 
will then assume that the sites that are not mentioned are created in the 
default state, ie they appear free of any bond and their internal state (if 
any) is the first of the list shown in the signature". This is the same as is 
being proposed here.

Original comment by lh64@cornell.edu on 26 Apr 2013 at 4:46

dweindl commented 3 years ago

Hi! Having better support for species synthesis rules as suggested above would be super helpful. Especially supporting creating copies based on some template (Cell()%x -> Cell()%x + Cell(x) above). I see that the issue is already 8 years old, but is there any chance that there will be better support for synthesis reactions anytime soon?

jrfaeder commented 3 years ago

Thanks for your interest and comment. Will look into this shortly and get back to you in more detail.

jrfaeder commented 3 years ago

OK, I had a longer look at this and I think that it should be possible to implement a modified version of this proposal relatively easily. Instantiating molecules with components in a default state should be very straightforward. Implementing a duplicate function where new molecules inherit from a template is a bit more complicated. I will have to do a bit more research on the code to see exactly what is needed. We need to either a new operation, copyMolecule, or to modify an existing operation, addMolecule, to work from a defined template. The syntax above would be a nightmare to implement in part because of the namespace issues -- it would also complicate the parser quite a bit -- so I'd prefer to avoid it. Instead I would use repeated instances of the tag to denote molecule copies. So cell division with component state inheritance would look like Cell()%x -> Cell()%x + Cell()%x Both cells would inherit the component state values of the Cell molecule on the left hand side. The tricky question would be if Cell is bound to other molecules, would those and the associated bonds also be copied? I tend to think yes for maximum flexibility, but open to suggestions on that. Modifications of components would also be allowed on the product side. The examples above would be recast as Cell()%x -> Cell()%x + Cell(a~0,b~1)%x or Cell(s~old)%x -> Cell(s~new)%x + Cell(s~new)%x

I think this would be relatively easy to implement in BNG. Implementation in NFsim would also be straightforward, but would take longer because I am not as familiar with the code and it tends to be more complex.

Let us know your thoughts.

dweindl commented 3 years ago

Thanks for the quick response, Jim.

So cell division with component state inheritance would look like Cell()%x -> Cell()%x + Cell()%x

I agree that this syntax would be preferable.

The tricky question would be if Cell is bound to other molecules, would those and the associated bonds also be copied? I tend to think yes [...]

Good point. I tend to agree. This seems like the most intuitive behavior to me.

Modifications of components would also be allowed on the product side.

That would be great, yes.

For me personally, it is about supporting such a syntax for the network expansion - so having it in BNG would already suffice.

lh64 commented 3 years ago

@jrfaeder, would duplicating a tag twice on the product side of a rule, as in Cell()%x -> Cell()%x + Cell()%x, complicate the mapping between reactants and products that tags are designed for? It seems that doing this would require adding additional logic to how reactants and products are mapped, i.e., a reactant tag maps to the first instance of the same tag on the product side of a rule; any other instances of the tag are considered copies of the tagged reactant molecule. It seems that this opens up other complications as well, such as what to do in cases like A()%x -> A()%x + B()%x + C()%x. How would something like this be interpreted? Do B() and C() inherit the component states of A()? Or would it be considered bad syntax and produce an error? That would require modifying the BNGL parser, which seems painful.

To me, a cleaner solution is to use the tag via some kind of built-in function, like _copyOf(), to extract the contents of a Molecule and use them to create another Molecule. So, maybe something like Cell()%x -> Cell()%x + _copyOf(x).

The issue of whether to copy bonds is a bit trickier. Maybe we can leverage prefix vs postfix tag convention for that purpose. So, for A()%x, which tags the Molecule A(), _copy(x) would only copy components and states, not bonds. But for %x:A(), which tags the Complex that contains A(), _copyOf(x) would reproduce the entire complex, bonds included.

It's good to see that this issue is getting attention again. Happy to discuss more and throw around ideas.

jrfaeder commented 3 years ago

@lh64: Great comments -- thanks!! This takes me back a few years... You raise important points about potential complications of my proposal. One reason I am inclined toward it is that I think it's the most minimal perturbation on the existing syntax. Even after all of these years we have not yet introduced any built-in functions, and while I think that they are probably needed eventually this use case doesn't strike me as the ideal place to introduce it. I agree that implementation would require modifying the mapping function and logic, but I think this is an easier part of the code to change potentially. The parser, don't talk to me about the parser.... We may have an easier-to-modify alternative on the horizon, so I'm not going to get too hung up on that at the moment. I'll dig into the code later today and report back within a few days.

dweindl commented 3 years ago

I'll dig into the code later today and report back within a few days.

Hi @jrfaeder, do you have any news on this?

jrfaeder commented 3 years ago

Ouch! No, unfortunately this slid off my radar screen, but thanks for the reminder. I know we are short on development bandwidth until early June (I was overly optimistic about what I might be able to get done over winter break). I think shortly after this we became completely distracted with developing our VS Code extension for BioNetGen, which if you have not checked out, you definitely should. We also have an associated command line interface and Python library for BioNetGen that is pip installable. Our queue is pretty full for May (the proposal for the renewal of the grant that supports BNG development is due at the end of May), but in the meantime it would help us if you could send a few examples of what you'd like to do, particularly in regards to copying objects. The syntax for creating Molecules/Species with default states is straightforward, but the syntax for copying is more difficult to get right, so restarting the conversation at this point could be helpful. Thanks for your patience.

dweindl commented 3 years ago

Thanks for the update. Fingers crossed for the grant renewal.

Here is a simple example of what I am trying to achieve:

begin model

begin molecule types
  A(s1~t~u,s2~t~u)
end molecule types

begin species
  A(s1~t,s2~u)   1
  A(s1~u,s2~t)   0.1
  # [...]
end species

begin reaction rules
  # It would be great to be able to create a copy of `A` without enumerating
  # all possible configurations, i.e. something giving a result equivalent to:

  r_explicit_0: A(s1~t,s2~u) -> A(s1~t,s2~u) + A(s1~t,s2~u) 1
  r_explicit_1: A(s1~u,s2~t) -> A(s1~u,s2~t) + A(s1~u,s2~t) 1
  # [...]

  # Tried the following:

  # r:  A -> A + A    1
  #
  # BioNetGen version 2.5.2
  # Reading from file test.bngl (level 0)
  # Read 1 molecule types.
  # Read 2 species.
  # ERROR: Molecule created in reaction rule: Component(s) s1 s2 missing from molecule A()
  #   at line 19
  # ABORT: Reaction rule list could not be read because of errors.

  # r:  A%x -> A%x + A%x 1
  #
  # BioNetGen version 2.5.2
  # Reading from file test.bngl (level 0)
  # Read 1 molecule types.
  # Read 2 species.
  # Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 372.
  # Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 372.
  # Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 376.
  # Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 376.
  # Missing argument in printf at [...]/bng-linux/Perl2/BNGModel.pm line 641.
  # ERROR: Molecule created in reaction rule: Component(s) s1 s2 missing from molecule A0()
  #   at line 19
  # ABORT: Reaction rule list could not be read because of errors.

  # r: A(s1%x,s2%y) -> A(s1%x,s2%y) + A(s1%x,s2%y)
  #
  # BioNetGen version 2.5.2
  # Reading from file test.bngl (level 0)
  # Read 1 molecule types.
  # Read 2 species.
  # Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 404.
  # Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 404.
  # Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 408.
  # Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 408.
  # Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 404.
  # Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 404.
  # Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 408.
  # Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 408.
  # ERROR: No RateLaw supplied for Reaction Rule.
  #   at line 21
  # ABORT: Reaction rule list could not be read because of errors.

end reaction rules

end model

begin actions
    generate_network({overwrite=>1,verbose=>1})
end actions
wshlavacek commented 3 years ago

I'm not exactly sure what's desired but if you want to simply randomize the internal state of a newly synthesized molecule, it's pretty easy to do that with a trick. See the BNGL file below. I've never used this trick before but it seems to be working as expected. I hope this is helpful --Bill

begin model

begin parameters

k_synth 1 # molecule per s

k_fast 100 # /s

k_faster 10000 # /s

end parameters

begin molecule types

X(a~0~1,b~0~1,c~0~1,new~T~F)

end molecule types

begin seed species

X(a~0,b~0,c~0,new~F) 0

end seed species

begin observables

Molecules Xa0b0c0 X(a~0,b~0,c~0,new~F)

Molecules Xa1b0c0 X(a~1,b~0,c~0,new~F)

Molecules Xa0b1c0 X(a~0,b~1,c~1,new~F)

Molecules Xa0b0c1 X(a~0,b~0,c~1,new~F)

Molecules Xa1b1c0 X(a~1,b~1,c~0,new~F)

Molecules Xa1b0c1 X(a~1,b~0,c~1,new~F)

Molecules Xa0b1c1 X(a~0,b~1,c~1,new~F)

Molecules Xa1b1c1 X(a~1,b~1,c~1,new~F)

end observables

begin functions

end functions

begin reaction rules

synthesis

0->X(a~0,b~0,c~0,new~T) k_synth

randomize the internal state of newly synthesized X

X(a~0,new~T)<->X(a~1,new~T) k_faster,k_faster

X(b~0,new~T)<->X(b~1,new~T) k_faster,k_faster

X(c~0,new~T)<->X(c~1,new~T) k_faster,k_faster

switch the state of X from new~T to new~F

X(new~T)->X(new~F) k_fast

end reaction rules

end model

begin actions

generate_network({overwrite=>1})

simulate({method=>"ode",t_end=>100,n_steps=>1000,print_functions=>1})

end actions

On Wed, Apr 21, 2021 at 2:40 AM Daniel Weindl @.***> wrote:

Thanks for the update. Fingers crossed for the grant renewal.

Here is a simple example of what I am trying to achieve:

begin model

begin molecule types A(s1~t~u,s2~t~u) end molecule types

begin species A(s1~t,s2~u) 1 A(s1~u,s2~t) 0.1

[...]

end species

begin reaction rules

It would be great to be able to create a copy of A without enumerating

all possible configurations, i.e. something giving a result equivalent to:

r_explicit_0: A(s1~t,s2~u) -> A(s1~t,s2~u) + A(s1~t,s2~u) 1 r_explicit_1: A(s1~u,s2~t) -> A(s1~u,s2~t) + A(s1~u,s2~t) 1

[...]

Tried the following:

r: A -> A + A 1

#

BioNetGen version 2.5.2

Reading from file test.bngl (level 0)

Read 1 molecule types.

Read 2 species.

ERROR: Molecule created in reaction rule: Component(s) s1 s2 missing from molecule A()

at line 19

ABORT: Reaction rule list could not be read because of errors.

r: A%x -> A%x + A%x 1

#

BioNetGen version 2.5.2

Reading from file test.bngl (level 0)

Read 1 molecule types.

Read 2 species.

Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 372.

Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 372.

Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 376.

Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 376.

Missing argument in printf at [...]/bng-linux/Perl2/BNGModel.pm line 641.

ERROR: Molecule created in reaction rule: Component(s) s1 s2 missing from molecule A0()

at line 19

ABORT: Reaction rule list could not be read because of errors.

r: A(s1%x,s2%y) -> A(s1%x,s2%y) + A(s1%x,s2%y)

#

BioNetGen version 2.5.2

Reading from file test.bngl (level 0)

Read 1 molecule types.

Read 2 species.

Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 404.

Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 404.

Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 408.

Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 408.

Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 404.

Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 404.

Use of uninitialized value in hash element at [...]/bng-linux/Perl2/RxnRule.pm line 408.

Use of uninitialized value within %labels in pattern match (m//) at [...]/bng-linux/Perl2/RxnRule.pm line 408.

ERROR: No RateLaw supplied for Reaction Rule.

at line 21

ABORT: Reaction rule list could not be read because of errors.

end reaction rules

end model

begin actions generate_network({overwrite=>1,verbose=>1}) end actions

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/RuleWorld/bionetgen/issues/58#issuecomment-823889379, or unsubscribe https://github.com/notifications/unsubscribe-auth/AETOR55C7ZXT63VIHKWUPBTTJ2FQTANCNFSM4UTSE35Q .

jrfaeder commented 3 years ago

Nice trick! We've used similar tricks for other purposes and this completely makes sense.

dweindl commented 3 years ago

Thanks for sharing, Bill. In my case it's not about randomizing, but good to know how that could be done.